IEDriveInfo.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. unit IEDriveInfo;
  2. {==================================================================
  3. Component TDriveInfo / Version 2.6 / January 2000
  4. ====================================================
  5. Description:
  6. ============
  7. Central drive management class. Provides information about all
  8. installed drives, for example wether drive is valid, disk is inserted
  9. displayname or volume size.
  10. Author:
  11. =======
  12. (c) Ingo Eckel 1999
  13. Sodener Weg 38
  14. 65812 Bad Soden
  15. Germany
  16. For detailed documentation and history see the documentation in TDriveInfo.htm.
  17. {==================================================================}
  18. {Required compiler options:}
  19. {$A+,B-,X+,H+,P+}
  20. interface
  21. uses
  22. Windows,
  23. Registry,
  24. SysUtils,
  25. Classes,
  26. ComCtrls,
  27. ShellApi,
  28. ShlObj,
  29. CommCtrl,
  30. Forms,
  31. BaseUtils;
  32. const
  33. {Flags used by TDriveInfo.ReadDriveStatus and TDriveView.RefreshRootNodes:}
  34. dsValid = 0; {checks only whether drive is still valid}
  35. dsImageIndex = 1; {Fetch imageindex, if not allready fetched}
  36. dsSize = 2; {Fetch disk size and serialnumber}
  37. dsDisplayName = 4; {Fetch drives displayname}
  38. dsAll = dsImageIndex or dsSize or dsDisplayName;
  39. FirstDrive = 'A';
  40. FirstFixedDrive = 'C';
  41. LastDrive = 'Z';
  42. type
  43. TDrive = Char;
  44. TDriveInfoRec = record
  45. PIDL : PItemIDList; {Fully qualyfied PIDL}
  46. Init : Boolean; {Drivestatus was updated once}
  47. Valid : Boolean; {Drivestatus is valid}
  48. DriveReady : Boolean; {Drive is ready}
  49. DriveType : Integer; {DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE}
  50. DisplayName : string; {Windows displayname}
  51. Prettyname : string; {Prettyfied displayname}
  52. LongPrettyName : string; {UNC-Network name on Network drives or same as PrettyName}
  53. DriveSerial : DWORD; {Serial number of the drive}
  54. Size : Int64; {Drivesize}
  55. ImageIndex : Integer; {Drive imageIndex}
  56. FileSystemName : string; {Filesystemname as returned by GetVolumeInformation}
  57. MaxFileNameLength : DWORD; {Maximum length of filenames}
  58. FileSystemFlags : DWORD; {Filesystem flags as returned by GetVolumeInformation}
  59. end;
  60. {---------------------------------------------------------------}
  61. TDriveInfo = Class(TObject)
  62. {---------------------------------------------------------------}
  63. Private
  64. {---------------------------------------------------------------}
  65. FData : Array[FirstDrive..LastDrive] Of TDriveInfoRec;
  66. FNoDrives : DWORD;
  67. FDesktop : IShellFolder;
  68. Function GetData(Drive : TDrive) : TDriveInfoRec;
  69. {---------------------------------------------------------------}
  70. Public
  71. {---------------------------------------------------------------}
  72. Property Data[Drive : TDrive] : TDriveInfoRec Read GetData; default;
  73. Function GetImageIndex(Drive : TDrive) : Integer;
  74. Function GetDisplayName(Drive : TDrive) : String;
  75. Function GetPrettyName(Drive : TDrive) : String;
  76. Function GetLongPrettyName(Drive : TDrive) : String;
  77. Function ReadDriveStatus(Drive : TDrive; Flags : Integer) : Boolean;
  78. Constructor Create;
  79. Destructor Destroy; Override;
  80. procedure Load;
  81. End;
  82. {---------------------------------------------------------------}
  83. Function GetShellFileName (Const Name : String ) : String; Overload;
  84. Function GetShellFileName (PIDL : PItemIDList) : String; OverLoad;
  85. Function GetNetWorkName (Drive : Char) : String;
  86. {Central drive information object instance of TDriveInfo}
  87. Var DriveInfo : TDriveInfo;
  88. ResourceString
  89. ErrorInvalidDrive = '%s is a invalid drive letter.';
  90. {---------------------------------------------------------------}
  91. implementation
  92. {---------------------------------------------------------------}
  93. uses
  94. Math;
  95. // ===========================================================
  96. // Class TDriveInfo:
  97. // ===========================================================
  98. Constructor TDriveInfo.Create;
  99. Begin
  100. Inherited Create;
  101. Load;
  102. End; {TDriveInfo.Create}
  103. destructor TDriveInfo.Destroy;
  104. var
  105. Drive: TDrive;
  106. begin
  107. for Drive := FirstDrive to LastDrive do
  108. with FData[Drive] do
  109. begin
  110. SetLength(DisplayName, 0);
  111. SetLength(PrettyName, 0);
  112. SetLength(LongPrettyName, 0);
  113. SetLength(FileSystemName, 0);
  114. // This causes access violation
  115. // FreePIDL(PIDL);
  116. end;
  117. inherited;
  118. end; {TDriveInfo.Destroy}
  119. procedure TDriveInfo.Load;
  120. var
  121. Drive: TDrive;
  122. Reg: TRegistry;
  123. begin
  124. FNoDrives := 0;
  125. Reg := TRegistry.Create;
  126. try
  127. if Reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') Then
  128. Reg.ReadBinaryData('NoDrives', FNoDrives, SizeOf(FNoDrives));
  129. Except
  130. Try
  131. FNoDrives := Reg.ReadInteger('NoDrives');
  132. Except
  133. End;
  134. End;
  135. Reg.Free;
  136. FDesktop := NIL;
  137. For Drive := FirstDrive To LastDrive Do
  138. With FData[Drive] Do
  139. Begin
  140. PIDL := NIL;
  141. Init := False;
  142. DriveType := Windows.GetDriveType(PChar(Drive + ':\'));
  143. Valid := Not Bool((1 SHL (Ord(Drive) - 65)) And FNoDrives)
  144. And (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
  145. Init := False;
  146. DriveReady := False;
  147. DisplayName := '';
  148. PrettyName := '';
  149. LongPrettyName := '';
  150. FileSystemName := '';
  151. DriveSerial := 0;
  152. Size := -1;
  153. ImageIndex := 0;
  154. FileSystemFlags := 0;
  155. MaxFileNameLength := 0;
  156. End;
  157. end;
  158. Function TDriveInfo.GetImageIndex(Drive : TDrive) : Integer;
  159. Begin
  160. if (Drive < FirstDrive) Or (Drive > LastDrive) then
  161. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  162. Result := 0;
  163. IF FData[Drive].Valid Then
  164. Begin
  165. IF (FData[Drive].ImageIndex = 0) Then
  166. ReadDriveStatus(Drive, dsImageIndex);
  167. Result := FData[Drive].ImageIndex;
  168. End;
  169. End; {TDriveInfo.GetImageIndex}
  170. Function TDriveInfo.GetDisplayName(Drive : TDrive) : String;
  171. Begin
  172. if (Drive < FirstDrive) Or (Drive > LastDrive) then
  173. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  174. Result := Drive + ':';
  175. IF FData[Drive].Valid Then
  176. Begin
  177. IF (Length(FData[Drive].DisplayName) = 0) Then
  178. ReadDriveStatus(Drive, dsDisplayName);
  179. Result := FData[Drive].DisplayName;
  180. End;
  181. End; {TDriveInfo.GetDisplayname}
  182. Function TDriveInfo.GetPrettyName(Drive : TDrive) : String;
  183. Begin
  184. if (Drive < FirstDrive) Or (Drive > LastDrive) then
  185. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  186. Result := Drive + ':';
  187. IF FData[Drive].Valid Then
  188. Begin
  189. IF (Length(FData[Drive].PrettyName) = 0) Then
  190. ReadDriveStatus(Drive, dsDisplayName);
  191. Result := FData[Drive].PrettyName;
  192. End;
  193. End; {TDriveInfo.GetPrettyName}
  194. Function TDriveInfo.GetLongPrettyName(Drive : TDrive) : String;
  195. Begin
  196. if (Drive < FirstDrive) Or (Drive > LastDrive) then
  197. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  198. Result := Drive + ':';
  199. IF FData[Drive].Valid Then
  200. Begin
  201. IF (Length(FData[Drive].PrettyName) = 0) Then
  202. ReadDriveStatus(Drive, dsDisplayName);
  203. Result := FData[Drive].LongPrettyName;
  204. End;
  205. End; {TDriveInfo.GetLongPrettyName}
  206. Function TDriveInfo.GetData(Drive: TDrive) : TDriveInfoRec;
  207. Begin
  208. if not (Upcase(Drive) in ['A'..'Z']) then
  209. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  210. Result := FData[Upcase(Drive)];
  211. End; {TDriveInfo.GetData}
  212. Function TDriveInfo.ReadDriveStatus(Drive : TDrive; Flags : Integer) : Boolean;
  213. var ErrorMode : word;
  214. FileInfo : TShFileInfo;
  215. FileSystemNameBuffer : String;
  216. DriveID : String;
  217. CPos : Integer;
  218. WStr : WideString;
  219. Eaten : ULONG;
  220. shAttr : ULONG;
  221. Begin
  222. If Not Assigned(FDesktop) Then
  223. SHGetDesktopFolder(FDesktop);
  224. Drive := Upcase(Drive);
  225. DriveID := '';
  226. if (Drive < FirstDrive) Or (Drive > LastDrive) then
  227. Raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  228. With FData[Drive] Do
  229. Begin
  230. Init := True;
  231. DriveType := Windows.GetDriveType(PChar(Drive + ':\'));
  232. Valid := Not Bool((1 SHL (Ord(Drive) - 65)) And FNoDrives)
  233. And (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
  234. IF Valid Then
  235. Begin
  236. IF Not Assigned(PIDL) And (Drive >= FirstFixedDrive) Then
  237. Begin
  238. WStr := Drive + ':\';
  239. FDesktop.ParseDisplayName(Application.Handle, NIL, PWideChar(WStr), Eaten, PIDL, ShAttr);
  240. End;
  241. {Read driveStatus:}
  242. IF (Flags And dsSize) <> 0 Then
  243. Begin
  244. { turn off critical errors }
  245. ErrorMode := SetErrorMode(SEM_FailCriticalErrors or SEM_NOOPENFILEERRORBOX);
  246. try
  247. { drive 1 = a, 2 = b, 3 = c, etc. }
  248. Size := BaseUtils.DiskSize(Ord(Drive) - $40);
  249. DriveReady := Size >= 0;
  250. IF DriveReady Then
  251. Begin
  252. SetLength(FilesystemNamebuffer,500) ;
  253. SetLength(DriveID, 24);
  254. {Access the physical drive:}
  255. If GetVolumeInformation(PChar(Drive + ':\'),
  256. PChar(DriveID), 24,
  257. @DriveSerial,
  258. MaxFileNameLength,
  259. FileSystemFlags,
  260. PChar(filesystemnamebuffer), 499) Then
  261. Begin
  262. FileSystemName := StrPas(PChar(FileSystemNameBuffer));
  263. DriveID := StrPas(PChar(DriveID));
  264. End
  265. Else
  266. Begin
  267. DriveSerial := 0;
  268. FileSystemName := '';
  269. End;
  270. SetLength(FileSystemNameBuffer, 0);
  271. End
  272. Else
  273. Begin
  274. DriveSerial := 0;
  275. End;
  276. finally { restore old error mode }
  277. SetErrorMode(ErrorMode);
  278. end;
  279. End;
  280. {DisplayName:}
  281. IF (Flags And dsDisplayName <> 0)Then
  282. Begin
  283. IF DriveReady or (Flags And dsSize = 0) Then
  284. Begin
  285. {Fetch drives displayname:}
  286. {Due to a bug in shGetFileInfo, this function returns allways the displayname of
  287. the first inserted disk, even if a disk change has occured. So, better use the
  288. Volume ID to build the drives displayname:}
  289. IF (DriveType = DRIVE_CDROM) And (Length(DriveID) > 0) Then
  290. DisplayName := DriveID[1] + LowerCase(Copy(DriveID, 2, 24)) + ' ('+ Drive + ':)'
  291. Else
  292. Begin
  293. IF Assigned(PIDL) Then
  294. DisplayName := GetShellFileName(PIDL)
  295. Else
  296. DisplayName := GetShellFileName(Drive + ':\')
  297. End;
  298. PrettyName := Drive + ': ' + DisplayName;
  299. CPos := Pos('(' + Drive, PrettyName);
  300. IF CPos > 0 Then
  301. SetLength(PrettyName, Pred(CPos));
  302. IF DriveType = DRIVE_REMOTE Then
  303. Begin
  304. DriveID := GetNetWorkName(Drive);
  305. PrettyName := Drive + ': ' + ExtractFileName(DriveID);
  306. LongPrettyName := Drive + ': ' + DriveID;
  307. End
  308. Else
  309. Begin
  310. LongPrettyName := Copy(PrettyName, 1, 3) + DisplayName;
  311. CPos := Pos('(' + Drive, LongPrettyName);
  312. IF CPos > 0 Then
  313. SetLength(LongPrettyName, Pred(CPos));
  314. End;
  315. End
  316. Else
  317. Begin
  318. DisplayName := Drive + ':';
  319. PrettyName := DisplayName;
  320. LongPrettyName := DisplayName;
  321. FreePIDL(PIDL);
  322. End;
  323. End;
  324. {ImageIndex:}
  325. IF ((Flags And dsImageIndex) <> 0) And (ImageIndex < 5) Then
  326. Begin
  327. IF Assigned(PIDL) Then
  328. SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON Or SHGFI_PIDL)
  329. Else
  330. SHGetFileInfo(PChar(Drive + ':\'), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  331. ImageIndex := FileInfo.iIcon;
  332. End;
  333. End
  334. Else
  335. Begin
  336. Size := 0;
  337. DriveReady := False;
  338. DisplayName := '';
  339. PrettyName := '';
  340. LongPrettyName := '';
  341. DriveSerial := 0;
  342. ImageIndex := 0;
  343. IF Assigned(PIDL) Then
  344. FreePIDL(PIDL);
  345. End;
  346. Result := Valid And DriveReady;
  347. End;
  348. End; {TDriveInfo.ReadDriveStatus}
  349. // ===========================================================
  350. // Other service functions and procedures:
  351. // ===========================================================
  352. // returns the filename as displayed by the shell
  353. Function GetShellFileName ( const Name : string ) : String;
  354. var sfi : TSHFileInfo;
  355. E : Integer;
  356. begin
  357. E := SetErrorMode(SEM_FAILCRITICALERRORS);
  358. Try
  359. If SHGetFileInfo(PChar( Name ), 0, sfi, SizeOf(TSHFileInfo), SHGFI_DISPLAYNAME) <> 0 Then
  360. Result := sfi.szDisplayName;
  361. finally
  362. SetErrorMode(E);
  363. End;
  364. end; {GetShellFileName}
  365. Function GetShellFileName (PIDL : PItemIDList) : String;
  366. var sfi : TSHFileInfo;
  367. E : Integer;
  368. begin
  369. E := SetErrorMode(SEM_FAILCRITICALERRORS);
  370. Try
  371. If SHGetFileInfo(PChar(PIDL), 0, sfi, SizeOf(TSHFileInfo), SHGFI_PIDL Or SHGFI_DISPLAYNAME) <> 0 Then
  372. Result := sfi.szDisplayName;
  373. finally
  374. SetErrorMode(E);
  375. End;
  376. end; {GetShellFileName}
  377. // Gets the network UNC-Name of a mounted drive:
  378. Function GetNetWorkName(Drive : Char) : String;
  379. Var P : Array[0..MAX_PATH] Of Char;
  380. MaxLen : DWORD;
  381. Begin
  382. MaxLen := MAX_PATH;
  383. IF WNetGetConnection(PChar(String(Drive + ':')), P, MaxLen) = NO_ERROR Then
  384. Result := P
  385. Else
  386. Result := '';
  387. End; {GetNetWorkName}
  388. // ======================================================
  389. // Initialization
  390. // ======================================================
  391. Initialization
  392. IF Not Assigned(DriveInfo) Then
  393. DriveInfo := TDriveInfo.Create;
  394. // ======================================================
  395. // Finalization
  396. // ======================================================
  397. Finalization
  398. IF Assigned(DriveInfo) Then
  399. Begin
  400. DriveInfo.Free;
  401. DriveInfo := NIL;
  402. End;
  403. end.