| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- unit IEDriveInfo;
- {==================================================================
- Component TDriveInfo / Version 2.6 / January 2000
- ====================================================
- Description:
- ============
- Central drive management class. Provides information about all
- installed drives, for example wether drive is valid, disk is inserted
- displayname or volume size.
- Author:
- =======
- (c) Ingo Eckel 1999
- Sodener Weg 38
- 65812 Bad Soden
- Germany
- For detailed documentation and history see the documentation in TDriveInfo.htm.
- {==================================================================}
- {Required compiler options:}
- {$A+,B-,X+,H+,P+}
- interface
- uses
- Windows, Registry, SysUtils, Classes, ComCtrls, ShellApi, ShlObj, CommCtrl, Forms,
- BaseUtils;
- const
- {Flags used by TDriveInfo.ReadDriveStatus and TDriveView.RefreshRootNodes:}
- dsValid = 0; {checks only whether drive is still valid}
- dsImageIndex = 1; {Fetch imageindex, if not allready fetched}
- dsSize = 2; {Fetch disk size and serialnumber}
- dsDisplayName = 4; {Fetch drives displayname}
- dsAll = dsImageIndex or dsSize or dsDisplayName;
- FirstDrive = 'A';
- FirstFixedDrive = 'C';
- LastDrive = 'Z';
- FirstSpecialFolder = CSIDL_DESKTOP;
- LastSpecialFolder = CSIDL_PRINTHOOD;
- type
- TDrive = Char;
- PDriveInfoRec = ^TDriveInfoRec;
- TDriveInfoRec = record
- PIDL : PItemIDList; {Fully qualyfied PIDL}
- Init : Boolean; {Drivestatus was updated once}
- Valid : Boolean; {Drivestatus is valid}
- DriveReady : Boolean; {Drive is ready}
- DriveType : Integer; {DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE}
- DisplayName : string; {Windows displayname}
- PrettyName : string; {Prettyfied displayname}
- DriveSerial : DWORD; {Serial number of the drive}
- Size : Int64; {Drivesize}
- ImageIndex : Integer; {Drive imageIndex}
- end;
- TSpecialFolder = FirstSpecialFolder..LastSpecialFolder;
- PSpecialFolderRec = ^TSpecialFolderRec;
- TSpecialFolderRec = record
- Valid: Boolean;
- Location: string;
- DisplayName: string;
- ImageIndex: Integer;
- PIDL: PItemIDList;
- end;
- TDriveInfo = class(TObject)
- private
- FData: array[FirstDrive..LastDrive] of TDriveInfoRec;
- FNoDrives: DWORD;
- FDesktop: IShellFolder;
- FFolders: array[TSpecialFolder] of TSpecialFolderRec;
- FHonorDrivePolicy: Boolean;
- function GetData(Drive: TDrive): PDriveInfoRec;
- function GetFolder(Folder: TSpecialFolder): PSpecialFolderRec;
- procedure ReadDriveBasicStatus(Drive: TDrive);
- procedure ResetDrive(Drive: TDrive);
- procedure SetHonorDrivePolicy(Value: Boolean);
- public
- property Data[Drive: TDrive]: PDriveInfoRec read GetData; default;
- property SpecialFolder[Folder: TSpecialFolder]: PSpecialFolderRec read GetFolder;
- function GetImageIndex(Drive: TDrive): Integer;
- function GetDisplayName(Drive: TDrive): string;
- function GetPrettyName(Drive: TDrive): string;
- function ReadDriveStatus(Drive: TDrive; Flags: Integer): Boolean;
- property HonorDrivePolicy: Boolean read FHonorDrivePolicy write SetHonorDrivePolicy;
- constructor Create;
- destructor Destroy; override;
- procedure Load;
- end;
- function GetShellFileName(const Name: string): string; overload;
- function GetShellFileName(PIDL: PItemIDList): string; overLoad;
- function GetNetWorkName(Drive: Char): string;
- function GetNetWorkConnected(Drive: Char): Boolean;
- {Central drive information object instance of TDriveInfo}
- var
- DriveInfo : TDriveInfo;
- resourceString
- ErrorInvalidDrive = '%s is a invalid drive letter.';
- implementation
- uses
- Math, PIDL;
- constructor TDriveInfo.Create;
- begin
- inherited;
- FHonorDrivePolicy := True;
- Load;
- end; {TDriveInfo.Create}
- destructor TDriveInfo.Destroy;
- var
- Drive: TDrive;
- begin
- for Drive := FirstDrive to LastDrive do
- with FData[Drive] do
- begin
- SetLength(DisplayName, 0);
- SetLength(PrettyName, 0);
- // This causes access violation
- // FreePIDL(PIDL);
- end;
- inherited;
- end; {TDriveInfo.Destroy}
- function TDriveInfo.GetFolder(Folder: TSpecialFolder): PSpecialFolderRec;
- var
- FileInfo: TShFileInfo;
- Path: PChar;
- Flags: Word;
- begin
- Assert((Folder >= Low(FFolders)) and (Folder <= High(FFolders)));
- with FFolders[Folder] do
- begin
- if not Valid then
- begin
- SpecialFolderLocation(Folder, Location, PIDL);
- if Assigned(PIDL) then
- begin
- Path := PChar(PIDL);
- Flags := SHGFI_PIDL;
- end
- else
- begin
- Path := PChar(Location);
- Flags := 0;
- end;
- SHGetFileInfo(Path, 0, FileInfo, SizeOf(FileInfo),
- SHGFI_DISPLAYNAME or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or Flags);
- ImageIndex := FileInfo.iIcon;
- DisplayName := FileInfo.szDisplayName;
- Valid := True;
- end;
- end;
- Result := @FFolders[Folder];
- end;
- procedure TDriveInfo.SetHonorDrivePolicy(Value: Boolean);
- var
- Drive: TDrive;
- begin
- if HonorDrivePolicy <> Value then
- begin
- FHonorDrivePolicy := Value;
- for Drive := FirstDrive to LastDrive do
- begin
- ReadDriveBasicStatus(Drive);
- end;
- end;
- end;
- procedure TDriveInfo.ReadDriveBasicStatus(Drive: TDrive);
- begin
- with FData[Drive] do
- begin
- DriveType := Windows.GetDriveType(PChar(Drive + ':\'));
- Valid :=
- ((not FHonorDrivePolicy) or (not Bool((1 shl (Ord(Drive) - 65)) and FNoDrives))) and
- (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
- end;
- end;
- procedure TDriveInfo.ResetDrive(Drive: TDrive);
- begin
- with FData[Drive] do
- begin
- DriveReady := False;
- DisplayName := '';
- PrettyName := '';
- DriveSerial := 0;
- Size := -1;
- ImageIndex := 0;
- end;
- end;
- procedure TDriveInfo.Load;
- var
- Drive: TDrive;
- Reg: TRegistry;
- Folder: TSpecialFolder;
- begin
- FNoDrives := 0;
- Reg := TRegistry.Create;
- try
- if Reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') Then
- Reg.ReadBinaryData('NoDrives', FNoDrives, SizeOf(FNoDrives));
- except
- try
- FNoDrives := Reg.ReadInteger('NoDrives');
- except
- end;
- end;
- Reg.Free;
- FDesktop := nil;
- for Drive := FirstDrive to LastDrive do
- begin
- with FData[Drive] do
- begin
- ReadDriveBasicStatus(Drive);
- Init := False;
- PIDL := nil;
- ResetDrive(Drive);
- end;
- end;
- for Folder := Low(FFolders) to High(FFolders) do
- FFolders[Folder].Valid := False;
- end;
- function TDriveInfo.GetImageIndex(Drive: TDrive): Integer;
- begin
- if (Drive < FirstDrive) or (Drive > LastDrive) then
- raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
- Result := 0;
- if FData[Drive].Valid then
- begin
- if FData[Drive].ImageIndex = 0 then
- ReadDriveStatus(Drive, dsImageIndex);
- Result := FData[Drive].ImageIndex;
- end;
- end; {TDriveInfo.GetImageIndex}
- function TDriveInfo.GetDisplayName(Drive: TDrive): string;
- begin
- if (Drive < FirstDrive) or (Drive > LastDrive) then
- raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
- Result := Drive + ':';
- if FData[Drive].Valid then
- begin
- if Length(FData[Drive].DisplayName) = 0 then
- ReadDriveStatus(Drive, dsDisplayName);
- Result := FData[Drive].DisplayName;
- end;
- end; {TDriveInfo.GetDisplayname}
- function TDriveInfo.GetPrettyName(Drive: TDrive): string;
- begin
- if (Drive < FirstDrive) or (Drive > LastDrive) then
- raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
- Result := Drive + ':';
- if FData[Drive].Valid then
- begin
- if Length(FData[Drive].PrettyName) = 0 then
- ReadDriveStatus(Drive, dsDisplayName);
- Result := FData[Drive].PrettyName;
- end;
- end; {TDriveInfo.GetPrettyName}
- function TDriveInfo.GetData(Drive: TDrive): PDriveInfoRec;
- begin
- if not CharInSet(Upcase(Drive), ['A'..'Z']) then
- raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
- Result := @FData[Upcase(Drive)];
- end; {TDriveInfo.GetData}
- function TDriveInfo.ReadDriveStatus(Drive: TDrive; Flags: Integer): Boolean;
- var
- ErrorMode: Word;
- FileInfo: TShFileInfo;
- DriveID: string;
- CPos: Integer;
- Eaten: ULONG;
- ShAttr: ULONG;
- MaxFileNameLength: DWORD;
- FileSystemFlags: DWORD;
- begin
- if not Assigned(FDesktop) then
- SHGetDesktopFolder(FDesktop);
- Drive := Upcase(Drive);
- if (Drive < FirstDrive) or (Drive > LastDrive) then
- raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
- with FData[Drive] do
- begin
- Init := True;
- ReadDriveBasicStatus(Drive);
- if Valid then
- begin
- if (not Assigned(PIDL)) and (Drive >= FirstFixedDrive) then
- begin
- if DriveType = DRIVE_REMOTE then
- begin
- ParseDisplayNameWithTimeout(FDesktop, Drive + ':\', PIDL);
- end
- else
- begin
- ShAttr := 0;
- FDesktop.ParseDisplayName(Application.Handle, nil, PChar(Drive + ':\'), Eaten, PIDL, ShAttr);
- end;
- end;
- {Read driveStatus:}
- if (Flags and dsSize) <> 0 then
- begin
- { turn off critical errors }
- ErrorMode := SetErrorMode(SEM_FailCriticalErrors or SEM_NOOPENFILEERRORBOX);
- try
- { drive 1 = a, 2 = b, 3 = c, etc. }
- Size := DiskSize(Ord(Drive) - $40);
- DriveReady := (Size >= 0);
- if DriveReady then
- begin
- {Access the physical drive:}
- if GetVolumeInformation(PChar(Drive + ':\'), nil, 0,
- @DriveSerial, MaxFileNameLength, FileSystemFlags,
- nil, 0) then
- begin
- end
- else
- begin
- DriveSerial := 0;
- end;
- end
- else
- begin
- DriveSerial := 0;
- end;
- finally
- { restore old error mode }
- SetErrorMode(ErrorMode);
- end;
- end;
- {DisplayName:}
- if (Flags and dsDisplayName <> 0) then
- begin
- {Fetch drives displayname:}
- if Assigned(PIDL) then DisplayName := GetShellFileName(PIDL)
- else
- if Drive < FirstFixedDrive then DisplayName := GetShellFileName(Drive + ':\')
- // typical reason we do not have PIDL is that it took too long to
- // call ParseDisplayName, in what case calling SHGetFileInfo with
- // path (instead of PIDL) will take long too, avoiding that and using
- // fallback
- else DisplayName := '(' + Drive + ':)';
- if DriveType <> DRIVE_REMOTE then
- begin
- PrettyName := Drive + ': ' + DisplayName;
- CPos := Pos(' (' + Drive + ':)', PrettyName);
- if CPos > 0 then
- Delete(PrettyName, CPos, 5);
- end
- else
- begin
- DriveID := GetNetWorkName(Drive);
- PrettyName := Drive + ': ' + ExtractFileName(DriveID);
- end;
- end;
- {ImageIndex:}
- if ((Flags and dsImageIndex) <> 0) and (ImageIndex < 5) then
- begin
- if Assigned(PIDL) then
- begin
- SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_PIDL)
- end
- else
- begin
- SHGetFileInfo(PChar(Drive + ':\'), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
- end;
- ImageIndex := FileInfo.iIcon;
- end;
- end
- else
- begin
- if Assigned(PIDL) then
- FreePIDL(PIDL);
- ResetDrive(Drive);
- end;
- Result := Valid and DriveReady;
- end;
- end; {TDriveInfo.ReadDriveStatus}
- function GetShellFileName(const Name: string): string;
- var
- SFI: TSHFileInfo;
- E: Integer;
- begin
- E := SetErrorMode(SEM_FAILCRITICALERRORS);
- try
- if SHGetFileInfo(PChar(Name), 0, SFI, SizeOf(TSHFileInfo), SHGFI_DISPLAYNAME) <> 0 then
- Result := SFI.szDisplayName;
- finally
- SetErrorMode(E);
- end;
- end; {GetShellFileName}
- function GetShellFileName(PIDL: PItemIDList): string;
- var
- SFI: TSHFileInfo;
- E: Integer;
- begin
- E := SetErrorMode(SEM_FAILCRITICALERRORS);
- try
- if SHGetFileInfo(PChar(PIDL), 0, SFI, SizeOf(TSHFileInfo), SHGFI_PIDL or SHGFI_DISPLAYNAME) <> 0 then
- Result := SFI.szDisplayName;
- finally
- SetErrorMode(E);
- end;
- end; {GetShellFileName}
- function GetNetWorkName(Drive: Char): string;
- var
- P: array[0..MAX_PATH] of Char;
- MaxLen : DWORD;
- begin
- MaxLen := MAX_PATH;
- if WNetGetConnection(PChar(string(Drive + ':')), P, MaxLen) = NO_ERROR then
- Result := P
- else
- Result := '';
- end; {GetNetWorkName}
- type
- LPBYTE = ^BYTE;
- LMSTR = LPWSTR;
- NET_API_STATUS = DWORD;
- _USE_INFO_1 = record
- ui1_local: LMSTR;
- ui1_remote: LMSTR;
- ui1_password: LMSTR;
- ui1_status: DWORD;
- ui1_asg_type: DWORD;
- ui1_refcount: DWORD;
- ui1_usecount: DWORD;
- end;
- USE_INFO_1 = _USE_INFO_1;
- PUSE_INFO_1 = ^USE_INFO_1;
- LPVOID = Pointer;
- const
- USE_OK = 0;
- USE_PAUSED = 1;
- USE_SESSLOST = 2;
- USE_DISCONN = 2;
- USE_NETERR = 3;
- USE_CONN = 4;
- USE_RECONN = 5;
- function NetUseGetInfo(UncServerName: LMSTR; UseName: LMSTR; Level: DWORD; var BufPtr: LPBYTE): NET_API_STATUS; stdcall; external 'netapi32.dll';
- function NetApiBufferFree(Buffer: Pointer): DWORD; stdcall; external 'netapi32.dll';
- function GetNetWorkConnected(Drive: Char): Boolean;
- var
- BufPtr: LPBYTE;
- NetResult: Integer;
- begin
- NetResult := NetUseGetInfo(nil, PChar(Drive + ':'), 1, BufPtr);
- if NetResult = 0 then
- begin
- Result := (PUSE_INFO_1(BufPtr)^.ui1_status = USE_OK);
- NetApiBufferFree(LPVOID(BufPtr));
- end
- else
- begin
- // NetUseGetInfo works for DFS shares only, hence when it fails
- // we suppose different share type and fallback to "connected"
- Result := True;
- end;
- end;
- initialization
- if not Assigned(DriveInfo) then
- DriveInfo := TDriveInfo.Create;
- finalization
- if Assigned(DriveInfo) then
- begin
- DriveInfo.Free;
- DriveInfo := nil;
- end;
- end.
|