IEDriveInfo.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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, Registry, SysUtils, Classes, ComCtrls, ShellApi, ShlObj, CommCtrl, Forms,
  23. BaseUtils;
  24. const
  25. {Flags used by TDriveInfo.ReadDriveStatus and TDriveView.RefreshRootNodes:}
  26. dsValid = 0; {checks only whether drive is still valid}
  27. dsImageIndex = 1; {Fetch imageindex, if not allready fetched}
  28. dsSize = 2; {Fetch disk size and serialnumber}
  29. dsDisplayName = 4; {Fetch drives displayname}
  30. dsAll = dsImageIndex or dsSize or dsDisplayName;
  31. FirstDrive = 'A';
  32. FirstFixedDrive = 'C';
  33. LastDrive = 'Z';
  34. type
  35. TDrive = Char;
  36. TDriveInfoRec = record
  37. PIDL : PItemIDList; {Fully qualyfied PIDL}
  38. Init : Boolean; {Drivestatus was updated once}
  39. Valid : Boolean; {Drivestatus is valid}
  40. DriveReady : Boolean; {Drive is ready}
  41. DriveType : Integer; {DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE}
  42. DisplayName : string; {Windows displayname}
  43. Prettyname : string; {Prettyfied displayname}
  44. LongPrettyName : string; {UNC-Network name on Network drives or same as PrettyName}
  45. DriveSerial : DWORD; {Serial number of the drive}
  46. Size : Int64; {Drivesize}
  47. ImageIndex : Integer; {Drive imageIndex}
  48. FileSystemName : string; {Filesystemname as returned by GetVolumeInformation}
  49. MaxFileNameLength : DWORD; {Maximum length of filenames}
  50. FileSystemFlags : DWORD; {Filesystem flags as returned by GetVolumeInformation}
  51. end;
  52. TDriveInfo = class(TObject)
  53. private
  54. FData: array[FirstDrive..LastDrive] of TDriveInfoRec;
  55. FNoDrives: DWORD;
  56. FDesktop: IShellFolder;
  57. function GetData(Drive: TDrive): TDriveInfoRec;
  58. public
  59. property Data[Drive: TDrive]: TDriveInfoRec read GetData; default;
  60. function GetImageIndex(Drive: TDrive): Integer;
  61. function GetDisplayName(Drive: TDrive): string;
  62. function GetPrettyName(Drive: TDrive): string;
  63. function GetLongPrettyName(Drive: TDrive): string;
  64. function ReadDriveStatus(Drive: TDrive; Flags: Integer): Boolean;
  65. constructor Create;
  66. destructor Destroy; override;
  67. procedure Load;
  68. end;
  69. function GetShellFileName(const Name: string): string; overload;
  70. function GetShellFileName(PIDL: PItemIDList): string; overLoad;
  71. function GetNetWorkName(Drive: Char): string;
  72. function GetNetWorkConnected(Drive: Char): Boolean;
  73. {Central drive information object instance of TDriveInfo}
  74. var
  75. DriveInfo : TDriveInfo;
  76. resourceString
  77. ErrorInvalidDrive = '%s is a invalid drive letter.';
  78. implementation
  79. uses
  80. Math;
  81. constructor TDriveInfo.Create;
  82. begin
  83. inherited;
  84. Load;
  85. end; {TDriveInfo.Create}
  86. destructor TDriveInfo.Destroy;
  87. var
  88. Drive: TDrive;
  89. begin
  90. for Drive := FirstDrive to LastDrive do
  91. with FData[Drive] do
  92. begin
  93. SetLength(DisplayName, 0);
  94. SetLength(PrettyName, 0);
  95. SetLength(LongPrettyName, 0);
  96. SetLength(FileSystemName, 0);
  97. // This causes access violation
  98. // FreePIDL(PIDL);
  99. end;
  100. inherited;
  101. end; {TDriveInfo.Destroy}
  102. procedure TDriveInfo.Load;
  103. var
  104. Drive: TDrive;
  105. Reg: TRegistry;
  106. begin
  107. FNoDrives := 0;
  108. Reg := TRegistry.Create;
  109. try
  110. if Reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') Then
  111. Reg.ReadBinaryData('NoDrives', FNoDrives, SizeOf(FNoDrives));
  112. except
  113. try
  114. FNoDrives := Reg.ReadInteger('NoDrives');
  115. except
  116. end;
  117. end;
  118. Reg.Free;
  119. FDesktop := nil;
  120. for Drive := FirstDrive to LastDrive do
  121. with FData[Drive] do
  122. begin
  123. PIDL := nil;
  124. Init := False;
  125. DriveType := Windows.GetDriveType(PChar(Drive + ':\'));
  126. Valid := not Bool((1 shl (Ord(Drive) - 65)) and FNoDrives) and
  127. (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
  128. Init := False;
  129. DriveReady := False;
  130. DisplayName := '';
  131. PrettyName := '';
  132. LongPrettyName := '';
  133. FileSystemName := '';
  134. DriveSerial := 0;
  135. Size := -1;
  136. ImageIndex := 0;
  137. FileSystemFlags := 0;
  138. MaxFileNameLength := 0;
  139. end;
  140. end;
  141. function TDriveInfo.GetImageIndex(Drive: TDrive): Integer;
  142. begin
  143. if (Drive < FirstDrive) or (Drive > LastDrive) then
  144. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  145. Result := 0;
  146. if FData[Drive].Valid then
  147. begin
  148. if FData[Drive].ImageIndex = 0 then
  149. ReadDriveStatus(Drive, dsImageIndex);
  150. Result := FData[Drive].ImageIndex;
  151. end;
  152. end; {TDriveInfo.GetImageIndex}
  153. function TDriveInfo.GetDisplayName(Drive: TDrive): string;
  154. begin
  155. if (Drive < FirstDrive) or (Drive > LastDrive) then
  156. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  157. Result := Drive + ':';
  158. if FData[Drive].Valid then
  159. begin
  160. if Length(FData[Drive].DisplayName) = 0 then
  161. ReadDriveStatus(Drive, dsDisplayName);
  162. Result := FData[Drive].DisplayName;
  163. end;
  164. end; {TDriveInfo.GetDisplayname}
  165. function TDriveInfo.GetPrettyName(Drive: TDrive): string;
  166. begin
  167. if (Drive < FirstDrive) or (Drive > LastDrive) then
  168. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  169. Result := Drive + ':';
  170. if FData[Drive].Valid then
  171. begin
  172. if Length(FData[Drive].PrettyName) = 0 then
  173. ReadDriveStatus(Drive, dsDisplayName);
  174. Result := FData[Drive].PrettyName;
  175. end;
  176. end; {TDriveInfo.GetPrettyName}
  177. function TDriveInfo.GetLongPrettyName(Drive: TDrive): String;
  178. begin
  179. if (Drive < FirstDrive) or (Drive > LastDrive) then
  180. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  181. Result := Drive + ':';
  182. if FData[Drive].Valid then
  183. begin
  184. if Length(FData[Drive].PrettyName) = 0 then
  185. ReadDriveStatus(Drive, dsDisplayName);
  186. Result := FData[Drive].LongPrettyName;
  187. end;
  188. end; {TDriveInfo.GetLongPrettyName}
  189. function TDriveInfo.GetData(Drive: TDrive): TDriveInfoRec;
  190. begin
  191. if not (Upcase(Drive) in ['A'..'Z']) then
  192. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  193. Result := FData[Upcase(Drive)];
  194. end; {TDriveInfo.GetData}
  195. function TDriveInfo.ReadDriveStatus(Drive: TDrive; Flags: Integer): Boolean;
  196. var
  197. ErrorMode: Word;
  198. FileInfo: TShFileInfo;
  199. FileSystemNameBuffer: string;
  200. DriveID: string;
  201. CPos: Integer;
  202. WStr: WideString;
  203. Eaten: ULONG;
  204. ShAttr: ULONG;
  205. begin
  206. if not Assigned(FDesktop) then
  207. SHGetDesktopFolder(FDesktop);
  208. Drive := Upcase(Drive);
  209. DriveID := '';
  210. if (Drive < FirstDrive) or (Drive > LastDrive) then
  211. raise EConvertError.Create(Format(ErrorInvalidDrive, [Drive]));
  212. with FData[Drive] do
  213. begin
  214. Init := True;
  215. DriveType := Windows.GetDriveType(PChar(Drive + ':\'));
  216. Valid := not Bool((1 shl (Ord(Drive) - 65)) and FNoDrives) and
  217. (DriveType in [DRIVE_REMOVABLE, DRIVE_FIXED, DRIVE_CDROM, DRIVE_RAMDISK, DRIVE_REMOTE]);
  218. if Valid then
  219. begin
  220. if (not Assigned(PIDL)) and (Drive >= FirstFixedDrive) then
  221. begin
  222. WStr := Drive + ':\';
  223. FDesktop.ParseDisplayName(Application.Handle, nil, PWideChar(WStr), Eaten, PIDL, ShAttr);
  224. end;
  225. {Read driveStatus:}
  226. if (Flags and dsSize) <> 0 then
  227. begin
  228. { turn off critical errors }
  229. ErrorMode := SetErrorMode(SEM_FailCriticalErrors or SEM_NOOPENFILEERRORBOX);
  230. try
  231. { drive 1 = a, 2 = b, 3 = c, etc. }
  232. Size := BaseUtils.DiskSize(Ord(Drive) - $40);
  233. DriveReady := (Size >= 0);
  234. if DriveReady then
  235. begin
  236. SetLength(FileSystemNameBuffer, 500) ;
  237. SetLength(DriveID, 24);
  238. {Access the physical drive:}
  239. if GetVolumeInformation(PChar(Drive + ':\'), PChar(DriveID), 24,
  240. @DriveSerial, MaxFileNameLength, FileSystemFlags,
  241. PChar(FileSystemNameBuffer), 499) then
  242. begin
  243. FileSystemName := StrPas(PChar(FileSystemNameBuffer));
  244. DriveID := StrPas(PChar(DriveID));
  245. end
  246. else
  247. begin
  248. DriveSerial := 0;
  249. FileSystemName := '';
  250. end;
  251. SetLength(FileSystemNameBuffer, 0);
  252. end
  253. else
  254. begin
  255. DriveSerial := 0;
  256. end;
  257. finally
  258. { restore old error mode }
  259. SetErrorMode(ErrorMode);
  260. end;
  261. end;
  262. {DisplayName:}
  263. if (Flags and dsDisplayName <> 0) then
  264. begin
  265. if DriveReady or (Flags and dsSize = 0) then
  266. begin
  267. {Fetch drives displayname:}
  268. {Due to a bug in shGetFileInfo, this function returns allways the displayname of
  269. the first inserted disk, even if a disk change has occured. So, better use the
  270. Volume ID to build the drives displayname:}
  271. if (DriveType = DRIVE_CDROM) and (Length(DriveID) > 0) then
  272. DisplayName := DriveID[1] + LowerCase(Copy(DriveID, 2, 24)) + ' ('+ Drive + ':)'
  273. else
  274. begin
  275. if Assigned(PIDL) then DisplayName := GetShellFileName(PIDL)
  276. else DisplayName := GetShellFileName(Drive + ':\')
  277. end;
  278. PrettyName := Drive + ': ' + DisplayName;
  279. CPos := Pos('(' + Drive, PrettyName);
  280. if CPos > 0 then
  281. SetLength(PrettyName, Pred(CPos));
  282. if DriveType = DRIVE_REMOTE then
  283. begin
  284. DriveID := GetNetWorkName(Drive);
  285. PrettyName := Drive + ': ' + ExtractFileName(DriveID);
  286. LongPrettyName := Drive + ': ' + DriveID;
  287. end
  288. else
  289. begin
  290. LongPrettyName := Copy(PrettyName, 1, 3) + DisplayName;
  291. CPos := Pos('(' + Drive, LongPrettyName);
  292. if CPos > 0 then
  293. SetLength(LongPrettyName, Pred(CPos));
  294. end;
  295. end
  296. else
  297. begin
  298. DisplayName := Drive + ':';
  299. PrettyName := DisplayName;
  300. LongPrettyName := DisplayName;
  301. FreePIDL(PIDL);
  302. end;
  303. end;
  304. {ImageIndex:}
  305. if ((Flags and dsImageIndex) <> 0) and (ImageIndex < 5) then
  306. begin
  307. if Assigned(PIDL) then
  308. SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_PIDL)
  309. else
  310. SHGetFileInfo(PChar(Drive + ':\'), 0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  311. ImageIndex := FileInfo.iIcon;
  312. end;
  313. end
  314. else
  315. begin
  316. Size := 0;
  317. DriveReady := False;
  318. DisplayName := '';
  319. PrettyName := '';
  320. LongPrettyName := '';
  321. DriveSerial := 0;
  322. ImageIndex := 0;
  323. if Assigned(PIDL) then
  324. FreePIDL(PIDL);
  325. end;
  326. Result := Valid and DriveReady;
  327. end;
  328. end; {TDriveInfo.ReadDriveStatus}
  329. function GetShellFileName(const Name: string): string;
  330. var
  331. SFI: TSHFileInfo;
  332. E: Integer;
  333. begin
  334. E := SetErrorMode(SEM_FAILCRITICALERRORS);
  335. try
  336. if SHGetFileInfo(PChar(Name), 0, SFI, SizeOf(TSHFileInfo), SHGFI_DISPLAYNAME) <> 0 then
  337. Result := SFI.szDisplayName;
  338. finally
  339. SetErrorMode(E);
  340. end;
  341. end; {GetShellFileName}
  342. function GetShellFileName(PIDL: PItemIDList): string;
  343. var
  344. SFI: TSHFileInfo;
  345. E: Integer;
  346. begin
  347. E := SetErrorMode(SEM_FAILCRITICALERRORS);
  348. try
  349. if SHGetFileInfo(PChar(PIDL), 0, SFI, SizeOf(TSHFileInfo), SHGFI_PIDL or SHGFI_DISPLAYNAME) <> 0 then
  350. Result := SFI.szDisplayName;
  351. finally
  352. SetErrorMode(E);
  353. end;
  354. end; {GetShellFileName}
  355. function GetNetWorkName(Drive: Char): string;
  356. var
  357. P: array[0..MAX_PATH] of Char;
  358. MaxLen : DWORD;
  359. begin
  360. MaxLen := MAX_PATH;
  361. if WNetGetConnection(PChar(string(Drive + ':')), P, MaxLen) = NO_ERROR then
  362. Result := P
  363. else
  364. Result := '';
  365. end; {GetNetWorkName}
  366. type
  367. LPBYTE = ^BYTE;
  368. LMSTR = LPWSTR;
  369. NET_API_STATUS = DWORD;
  370. _USE_INFO_1 = record
  371. ui1_local: LMSTR;
  372. ui1_remote: LMSTR;
  373. ui1_password: LMSTR;
  374. ui1_status: DWORD;
  375. ui1_asg_type: DWORD;
  376. ui1_refcount: DWORD;
  377. ui1_usecount: DWORD;
  378. end;
  379. USE_INFO_1 = _USE_INFO_1;
  380. PUSE_INFO_1 = ^USE_INFO_1;
  381. LPVOID = Pointer;
  382. const
  383. USE_OK = 0;
  384. USE_PAUSED = 1;
  385. USE_SESSLOST = 2;
  386. USE_DISCONN = 2;
  387. USE_NETERR = 3;
  388. USE_CONN = 4;
  389. USE_RECONN = 5;
  390. var
  391. NetUseGetInfo: function(UncServerName: LMSTR; UseName: LMSTR; Level: DWORD;
  392. var BufPtr: LPBYTE): NET_API_STATUS; stdcall;
  393. NetApiBufferFree: function(Buffer: LPVOID): NET_API_STATUS; stdcall;
  394. function GetNetWorkConnected(Drive: Char): Boolean;
  395. var
  396. BufPtr: LPBYTE;
  397. Use: WideString;
  398. NetResult: Integer;
  399. begin
  400. if Assigned(NetUseGetInfo) then
  401. begin
  402. Use := Drive + ':';
  403. NetResult := NetUseGetInfo(nil, PWideChar(Use), 1, BufPtr);
  404. if NetResult = 0 then
  405. begin
  406. Result := (PUSE_INFO_1(BufPtr)^.ui1_status = USE_OK);
  407. Assert(Assigned(NetApiBufferFree));
  408. NetApiBufferFree(LPVOID(BufPtr));
  409. end
  410. else
  411. begin
  412. // NetUseGetInfo works for DFS shares only, hence when it fails
  413. // we suppose different share type and fallback to "connected"
  414. Result := True;
  415. end;
  416. end
  417. else
  418. begin
  419. Result := True;
  420. end;
  421. end;
  422. var
  423. NetApiDll: THandle;
  424. initialization
  425. NetApiDll := SafeLoadLibrary('netapi32.dll');
  426. if NetApiDll <> 0 then
  427. begin
  428. @NetUseGetInfo := GetProcAddress(NetApiDll, 'NetUseGetInfo');
  429. @NetApiBufferFree := GetProcAddress(NetApiDll, 'NetApiBufferFree');
  430. end
  431. else
  432. begin
  433. @NetUseGetInfo := nil;
  434. @NetApiBufferFree := nil;
  435. end;
  436. if not Assigned(DriveInfo) then
  437. DriveInfo := TDriveInfo.Create;
  438. finalization
  439. if Assigned(DriveInfo) then
  440. begin
  441. DriveInfo.Free;
  442. DriveInfo := nil;
  443. end;
  444. if NetApiDll <> 0 then FreeLibrary(NetApiDll);
  445. end.