Subversion Repositories delphiutils

Rev

Rev 6 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. ////////////////////////////////////////////////////////////////////////////////////
  2. // RECYCLE-BIN-FUNCTIONS BY DANIEL MARSCHALL                                      //
  3. // E-MAIL: info@daniel-marschall.de                                               //
  4. // WEB:    www.daniel-marschall.de                                                //
  5. ////////////////////////////////////////////////////////////////////////////////////
  6. // Revision: 16 JUN 2010                                                          //
  7. // This unit is freeware, but please link to my website if you are using it!      //
  8. ////////////////////////////////////////////////////////////////////////////////////
  9. // Successful tested with:                                                        //
  10. // Windows 95b, 95b + IE4, 98-SE, XP-SP2, 2000-SP4, 2003-SRV-EE-SP1, VISTA        //
  11. ////////////////////////////////////////////////////////////////////////////////////
  12. //                                                                                //
  13. //  Needs Delphi 4 or higher. If you are using Delphi 4 or 5, you can not use the //
  14. //  RecyclerGetDateTime() functions, because the unit "DateUtils" is missing.     //
  15. //  Warning! This is a platform unit.                                             //
  16. //                                                                                //
  17. //  To do! Can you help?                                                          //
  18. //    - WideString-Support (input/output)                                         //
  19. //    - GetLoginNameW: Really 255 maximum?                                        //
  20. //    - Always do EOF before reading anything?                                    //
  21. //    - Is it possible to identify a Vista-file that is not named $Ixxxxxx.ext?   //
  22. //    - RecyclerGetInfofiles() check additionally for removable device?           //
  23. //      RecyclerIsValid() is false.                                               //
  24. //    - Function to get current state of recycle-bin (full, empty)                //
  25. //    - Make functions to get the correct icons of full, empty, current           //
  26. //                                                                                //
  27. //  Unknown! Do you know the answer?                                              //
  28. //    - How does Windows 9x/NT manage the daylight saving time (if it does)?      //
  29. //    - How does Windows Vista react to a RECYCLER\ folder on a NTFS device?      //
  30. //    - How does Windows Vista react to a RECYCLED\ folder on a FAT device?       //
  31. //                                                                                //
  32. //  Not analyzed yet! Please send me your trash folder contents for analyzing!    //
  33. //    - Windows NT                                                                //
  34. //    - Windows CE?                                                               //
  35. //    - Windows 7                                                                 //
  36. //                                                                                //
  37. //  Thanks to all these who have helped me solving coding problems.               //
  38. //  Thanks to SEBA for sending in the Windows Vista trash structure files.        //
  39. //  Thanks to OMATA for testing the unit with Delphi 4.                           //
  40. //  Thanks to DEITYSOU for making a bugfix of DriveExists()                       //
  41. //                                                                                //
  42. ////////////////////////////////////////////////////////////////////////////////////
  43.  
  44. (*
  45.  
  46. == TODO LISTE ==
  47.  
  48. Wichtig! Windows XP: InfoTip, IntroText und LocalizedString sind Resourcenangaben und müssen ausgelesen werden!
  49. Testen: Wie reagiert Windows, wenn Bitbucket\C existiert, aber kein Wert 'Percent' hat? Mit der Standardeinstellung?
  50. Bug: Windows 2000 bei bestehenden Windows 95 Partition: Recycler Filename ist dann Recycled und nicht Recycler!
  51. bug? w95 recycled file hat immer selben löschzeitpunkt und größe? war die nicht verschieden?
  52. Feature: win7 support
  53. beachtet? bei leerem papierkorb auf fat ist weder info noch info2 vorhanden?
  54. testen: auch möglich, einen vista papierkorb offline öffnen?
  55. Problem: bei win95(ohne ie4) und win2000 gleichzeitiger installation: es existiert info UND info2!!!
  56. Neue Funktionen: GetCurrentRecyclerIcon, GetFullIcon, GetEmptyIcon
  57. Bei Vista gibt es manchmal einen c0000013 Fehler. (Auslesen des ganzen Papierkorbs)
  58. Windows 7 miteinbeziehen!
  59. Implement SETTER functions to every kind of configuration thing. (percentage etc)
  60. Registry CURRENT_USER: Funktionen auch für fremde Benutzer zur Verfügung stellen?
  61.  
  62. - Future -
  63.  
  64. Demoapplikation: Dateien statt als Text als Explorer-Like?
  65. Einzelne Elemente oder alle wiederherstellen oder löschen
  66. Konfiguration für Laufwerke ändern etc
  67. IconString -> TIcon Convertion functions
  68. platzreservierung in mb-angabe berechnen
  69. I don't know if there exists any API function which checks the state at any internal way.
  70.  
  71. *)
  72.  
  73. // TODO: Also include BC++ Versions
  74. {$IFNDEF BCB}
  75. {$DEFINE DEL1UP}
  76. {$IFNDEF VER80}
  77. {$DEFINE DEL2UP}
  78. {$IFNDEF VER90}
  79. {$DEFINE DEL3UP}
  80. {$IFNDEF VER100}
  81. {$DEFINE DEL4UP}
  82. {$IFNDEF VER120}
  83. {$DEFINE DEL5UP}
  84. {$IFNDEF VER130}
  85. {$DEFINE DEL6UP}
  86. {$IFNDEF VER140}
  87. {$DEFINE DEL7UP}
  88. {$ENDIF}
  89. {$ENDIF}
  90. {$ENDIF}
  91. {$ENDIF}
  92. {$ENDIF}
  93. {$ENDIF}
  94. {$ENDIF}
  95.  
  96. {$IFDEF DEL7UP}
  97. {$WARN UNSAFE_TYPE OFF}
  98. {$WARN UNSAFE_CODE OFF}
  99. {$WARN UNSAFE_CAST OFF}
  100. {$ENDIF}
  101.  
  102. {$IFDEF DEL6UP}
  103. unit RecyclerFunctions platform;
  104. {$ELSE}
  105. unit RecyclerFunctions;
  106. {$ENDIF}
  107.  
  108. // Configuration
  109.  
  110. // If enabled, all functions with parameter "InfofileOrRecycleFolder" will
  111. // also accept files which are not the indexfile (then, a INFO2 or INFO file
  112. // will be searched in this directory).
  113. {.$DEFINE allow_all_filenames}
  114.  
  115. interface
  116.  
  117. uses
  118.   Windows, SysUtils, Classes, {$IFDEF DEL6UP}DateUtils,{$ENDIF}
  119.   ShellApi{$IFNDEF DEL6UP}, FileCtrl{$ENDIF}, Registry,
  120.   BitOps, Messages;
  121.  
  122. type
  123.   EUnknownState = class(Exception);
  124.   EEventCategoryNotDefined = class(Exception);
  125.   EAPICallError = class(Exception);
  126.  
  127.   PSHQueryRBInfo = ^TSHQueryRBInfo;
  128.   TSHQueryRBInfo = packed record
  129.     cbSize      : dword;
  130.     i64Size     : int64;
  131.     i64NumItems : int64;
  132.   end;
  133.  
  134. const
  135.   RECYCLER_CLSID = '{645FF040-5081-101B-9F08-00AA002F954E}';
  136.  
  137. {$IFDEF DEL6UP}
  138. function RecyclerGetDateTime(drive: char; fileid: string): tdatetime; overload;
  139. function RecyclerGetDateTime(drive: char; UserSID: string; fileid: string): tdatetime; overload;
  140. function RecyclerGetDateTime(InfofileOrRecycleFolder: string): tdatetime; overload;
  141. function RecyclerGetDateTime(InfofileOrRecycleFolder: string; id: string): tdatetime; overload;
  142. {$ENDIF}
  143.  
  144. function RecyclerGetSourceUnicode(drive: char; fileid: string): WideString; overload;
  145. function RecyclerGetSourceUnicode(drive: char; UserSID: string; fileid: string): WideString; overload;
  146. function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string): WideString; overload;
  147. function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string; id: string): WideString; overload;
  148.  
  149. function RecyclerGetSource(drive: char; fileid: string): string; overload;
  150. function RecyclerGetSource(drive: char; UserSID: string; fileid: string): string; overload;
  151. function RecyclerGetSource(InfofileOrRecycleFolder: string): string; overload;
  152. function RecyclerGetSource(InfofileOrRecycleFolder: string; id: string): string; overload;
  153.  
  154. procedure RecyclerListIndexes(drive: char; result: TStringList); overload;
  155. procedure RecyclerListIndexes(drive: char; UserSID: string; result: TStringList); overload;
  156. procedure RecyclerListIndexes(InfofileOrRecycleFolder: string; result: TStringList); overload;
  157.  
  158. function RecyclerGetSourceDrive(drive: char; fileid: string): char; overload;
  159. function RecyclerGetSourceDrive(drive: char; UserSID: string; fileid: string): char; overload;
  160. function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string): char; overload;
  161. function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string; id: string): char; overload;
  162.  
  163. function RecyclerOriginalSize(drive: char; fileid: string): integer; overload;
  164. function RecyclerOriginalSize(drive: char; UserSID: string; fileid: string): integer; overload;
  165. function RecyclerOriginalSize(InfofileOrRecycleFolder: string): integer; overload;
  166. function RecyclerOriginalSize(InfofileOrRecycleFolder: string; id: string): integer; overload;
  167.  
  168. function RecyclerIsValid(drive: char): boolean; overload;
  169. function RecyclerIsValid(drive: char; UserSID: string): boolean; overload;
  170. function RecyclerIsValid(InfofileOrRecycleFolder: string): boolean; overload;
  171.  
  172. function RecyclerCurrentFilename(drive: char; fileid: string): string; overload;
  173. function RecyclerCurrentFilename(drive: char; UserSID: string; fileid: string): string; overload;
  174. function RecyclerCurrentFilename(InfofileOrRecycleFolder: string): string; overload;
  175. function RecyclerCurrentFilename(InfofileOrRecycleFolder: string; id: string): string; overload;
  176.  
  177. function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string): string; overload;
  178. function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean): string; overload;
  179. function RecyclerGetPath(drive: char; IncludeInfofile: boolean): string; overload;
  180. function RecyclerGetPath(drive: char; UserSID: string): string; overload;
  181. function RecyclerGetPath(drive: char): string; overload;
  182.  
  183. procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string; result: TStringList); overload;
  184. procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; result: TStringList); overload;
  185. procedure RecyclerGetInfofiles(drive: char; IncludeInfofile: boolean; result: TStringList); overload;
  186. procedure RecyclerGetInfofiles(drive: char; UserSID: string; result: TStringList); overload;
  187. procedure RecyclerGetInfofiles(drive: char; result: TStringList); overload;
  188.  
  189. function RecyclerCurrentFilenameAndPath(drive: char; UserSID: string; fileid: string): string; overload;
  190. function RecyclerCurrentFilenameAndPath(drive: char; fileid: string): string; overload;
  191. function RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder: string; id: string): string; overload;
  192.  
  193. function RecyclerRemoveItem(drive: char; UserSID: string; fileid: string): boolean; overload;
  194. function RecyclerRemoveItem(drive: char; fileid: string): boolean; overload;
  195. function RecyclerRemoveItem(InfofileOrRecycleFolder: string; id: string): boolean; overload;
  196.  
  197. procedure RecyclerGetAllRecyclerDrives(result: TStringList);
  198.  
  199. function RecyclerEmptyRecycleBin(flags: cardinal): boolean; overload;
  200. function RecyclerEmptyRecycleBin(sound, progress, confirmation: boolean): boolean; overload;
  201.  
  202. function RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean): boolean; overload;
  203. function RecyclerAddFileOrFolder(FileOrFolder: string): boolean; overload;
  204.  
  205. function RecyclerConfirmationDialogEnabled: boolean;
  206. procedure RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
  207.  
  208. function RecyclerGetCurrentIconString: string;
  209. function RecyclerGetDefaultIconString: string;
  210. function RecyclerGetEmptyIconString: string;
  211. function RecyclerGetFullIconString: string;
  212.  
  213. function RecyclerGetName: string;
  214. function RecyclerGetInfoTip: string;
  215. function RecyclerGetIntroText: string;
  216.  
  217. function RecyclerEmptyEventGetName: string;
  218. function RecyclerEmptyEventGetCurrentSound: string;
  219. function RecyclerEmptyEventGetDefaultSound: string;
  220. procedure RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
  221. function RecyclerEmptyEventGetSound(ACategory: string): string;
  222.  
  223. function RecyclerGlobalGetPercentUsage: integer;
  224. function RecyclerSpecificGetPercentUsage(Drive: Char): integer;
  225. function RecyclerGetPercentUsageAutoDeterminate(Drive: Char): integer;
  226.  
  227. function RecyclerGlobalIsNukeOnDelete: boolean;
  228. function RecyclerSpecificIsNukeOnDelete(Drive: Char): boolean;
  229. function RecyclerIsNukeOnDeleteAutoDeterminate(Drive: Char): boolean;
  230.  
  231. function RecyclerHasGlobalSettings: boolean;
  232.  
  233. function RecyclerIsEmpty: boolean; overload;
  234. function RecyclerIsEmpty(Drive: Char): boolean; overload;
  235.  
  236. function RecyclerGetNumItems: int64; overload;
  237. function RecyclerGetNumItems(Drive: Char): int64; overload;
  238.  
  239. function RecyclerGetSize: int64; overload;
  240. function RecyclerGetSize(Drive: Char): int64; overload;
  241.  
  242. function RecyclerGetAPIInfo(Drive: Char): TSHQueryRBInfo; overload;
  243. function RecyclerGetAPIInfo(Path: String): TSHQueryRBInfo; overload;
  244.  
  245. function RecyclerGetCLSID: string;
  246.  
  247. // Diese Funktion ist false, wenn sie z.B. unter Windows 95 ohne Internet Explorer
  248. // 4.0 Shell Extension ausgeführt wird. Wenn abwärtskompatibler Code geschrieben
  249. // werden soll, sollte RecyclerQueryFunctionAvailable() verwendet werden, da
  250. // unter Windows 95 folgende Funktionalitäten NICHT vorhanden sind:
  251. // - RecyclerIsEmpty
  252. // - RecyclerGetNumItems
  253. // - RecyclerGetSize
  254. // - RecyclerGetAPIInfo
  255. function RecyclerQueryFunctionAvailable: boolean;
  256.  
  257. function RecyclerLibraryVersion: string;
  258.  
  259. implementation
  260.  
  261. type
  262.   SHELLSTATE = record
  263.     Flags1: DWORD;
  264. (*
  265.     BOOL fShowAllObjects : 1;
  266.     BOOL fShowExtensions : 1;
  267.     BOOL fNoConfirmRecycle : 1;
  268.  
  269.     BOOL fShowSysFiles : 1;
  270.     BOOL fShowCompColor : 1;
  271.     BOOL fDoubleClickInWebView : 1;
  272.     BOOL fDesktopHTML : 1;
  273.     BOOL fWin95Classic : 1;
  274.     BOOL fDontPrettyPath : 1;
  275.     BOOL fShowAttribCol : 1; // No longer used, dead bit
  276.     BOOL fMapNetDrvBtn : 1;
  277.     BOOL fShowInfoTip : 1;
  278.     BOOL fHideIcons : 1;
  279.     BOOL fWebView : 1;
  280.     BOOL fFilter : 1;
  281.     BOOL fShowSuperHidden : 1;
  282.     BOOL fNoNetCrawling : 1;
  283. *)
  284.     dwWin95Unused: DWORD; // Win95 only - no longer supported pszHiddenFileExts
  285.     uWin95Unused: UINT; // Win95 only - no longer supported cbHiddenFileExts
  286.  
  287.     // Note: Not a typo!  This is a persisted structure so we cannot use LPARAM
  288.     lParamSort: Integer;
  289.     iSortDirection: Integer;
  290.  
  291.     version: UINT;
  292.  
  293.     // new for win2k. need notUsed var to calc the right size of ie4 struct
  294.     // FIELD_OFFSET does not work on bit fields
  295.     uNotUsed: UINT; // feel free to rename and use
  296.     Flags2: DWORD;
  297. (*
  298.     BOOL fSepProcess: 1;
  299.     // new for Whistler.
  300.     BOOL fStartPanelOn: 1;       //Indicates if the Whistler StartPanel mode is ON or OFF.
  301.     BOOL fShowStartPage: 1;      //Indicates if the Whistler StartPage on desktop is ON or OFF.
  302.     UINT fSpareFlags : 13;
  303. *)
  304.   end;
  305.   LPSHELLSTATE = ^SHELLSTATE;
  306.  
  307. const
  308.   {$IFDEF MSWINDOWS}
  309.     shell32  = 'shell32.dll';
  310.     advapi32 = 'advapi32.dll';
  311.   {$ENDIF}
  312.   {$IFDEF LINUX}
  313.     shell32  = 'libshell32.borland.so';
  314.     advapi32 = 'libwine.borland.so';
  315.   {$ENDIF}
  316.  
  317.   // Masks for the shellstate
  318.    SSF_SHOWALLOBJECTS  = $00000001;
  319.   SSF_SHOWEXTENSIONS  = $00000002;
  320.   SSF_HIDDENFILEEXTS  = $00000004;
  321.   SSF_SERVERADMINUI   = $00000004;
  322.   SSF_SHOWCOMPCOLOR   = $00000008;
  323.   SSF_SORTCOLUMNS     = $00000010;
  324.   SSF_SHOWSYSFILES    = $00000020;
  325.   SSF_DOUBLECLICKINWEBVIEW = $00000080;
  326.   SSF_SHOWATTRIBCOL   = $00000100;
  327.   SSF_DESKTOPHTML     = $00000200;
  328.   SSF_WIN95CLASSIC    = $00000400;
  329.   SSF_DONTPRETTYPATH  = $00000800;
  330.   SSF_SHOWINFOTIP     = $00002000;
  331.   SSF_MAPNETDRVBUTTON = $00001000;
  332.   SSF_NOCONFIRMRECYCLE = $00008000;
  333.   SSF_HIDEICONS       = $00004000;
  334.   SSF_FILTER          = $00010000;
  335.   SSF_WEBVIEW         = $00020000;
  336.   SSF_SHOWSUPERHIDDEN = $00040000;
  337.   SSF_SEPPROCESS      = $00080000;
  338.   SSF_NONETCRAWLING   = $00100000;
  339.   SSF_STARTPANELON    = $00200000;
  340.   SSF_SHOWSTARTPAGE   = $00400000;
  341.  
  342. // **********************************************************
  343. // COMPATIBILITY FUNCTIONS
  344. // **********************************************************
  345.  
  346. {$IFNDEF DEL5UP}
  347. function IncludeTrailingBackslash(str: string): string;
  348. begin
  349.   if Copy(str, length(str), 1) = '\' then    // TODO? Gibt es PathDelim in Delphi 4?
  350.     Result := str
  351.   else
  352.     Result := str + '\';
  353. end;
  354. {$ENDIF}
  355.  
  356. // **********************************************************
  357. // INTERNALLY USED FUNCTIONS
  358. // **********************************************************
  359.  
  360. type
  361.   TNibble = $0..$F;
  362.   TBitPos = 0..7;
  363.  
  364. resourcestring
  365.   LNG_UNEXPECTED_STATE = 'Cannot determinate state of "%s" because of an unknown value in the configuration of your operation system. Please contact the developer of the Recycler Bin Unit and help improving the determination methods!';
  366.   LNG_API_CALL_ERROR = 'Error while calling the API. Additional information: "%s".';
  367.   LNG_NOT_CALLABLE = '%s not callable';
  368.   LNG_ERROR_CODE = '%s returns error code %s';
  369.  
  370. function _GetBit(B: Byte; BitPos: TBitPos): boolean; overload;
  371. var
  372.   p: byte;
  373. begin
  374.   p := 1 shl BitPos; // 2 ^ BitPos
  375.   result := B and p = p;
  376. end;
  377.  
  378. function _GetBit(B: Char; BitPos: TBitPos): boolean; overload;
  379. begin
  380.   result := _GetBit(Ord(B), BitPos);
  381. end;
  382.  
  383. function _DriveNum(Drive: Char): Byte;
  384. // a->0, ..., z->25
  385. var
  386.   tmp: string;
  387. begin
  388.   tmp := LowerCase(Drive);
  389.   result := Ord(tmp[1])-Ord('a');
  390. end;
  391.  
  392. function _registryReadDump(AReg: TRegistry; AName: string): string;
  393. const
  394.   // Win2000 RegEdit has set the max input length of a REG_BINARY to $3FFF.
  395.   // Probably its the longest possible binary string and not just a GUI limit.
  396.   BufMax = $3FFF;
  397. var
  398.   buf: array[0..BufMax] of byte;
  399.   i: integer;
  400.   realsize: integer;
  401. begin
  402.   realsize := AReg.ReadBinaryData(AName, buf, SizeOf(buf));
  403.  
  404.   for i := 0 to realsize-1 do
  405.   begin
  406.     result := result + chr(buf[i]);
  407.   end;
  408. end;
  409.  
  410. function _LowerNibble(B: Byte): TNibble;
  411. begin
  412.   result := B and 15 {00001111};
  413. end;
  414.  
  415. (* function _UpperNibble(B: Byte): TNibble;
  416. begin
  417.   result := B and 240 {11110000};
  418. end;
  419.  
  420. function _MakeByte(UpperNibble, LowerNibble: TNibble): Byte;
  421. begin
  422.   result := LowerNibble + UpperNibble * $10;
  423. end; *)
  424.  
  425. function _GetStringFromDLL(filename: string; num: integer): string;
  426. const
  427.   // http://www.eggheadcafe.com/forumarchives/vcmfc/sep2005/post23917443.asp
  428.   MAX_BUF = 4097; // OK?
  429. var
  430.   hLib: THandle;
  431.   buf: array[0..MAX_BUF] of char;
  432. begin
  433.   hLib := LoadLibrary(PChar(filename));
  434.   try
  435.     LoadString(hLib, num, buf, sizeof(buf));
  436.     result := buf;
  437.   finally
  438.     FreeLibrary(hLib);
  439.   end;
  440. end;
  441.  
  442. // http://www.delphi-library.de/topic_Umgebungsvariable+in+einem+String+aufloesen_20516,0.html
  443. function _ExpandEnvStr(const szInput: string): string;
  444. const
  445.   MAXSIZE = 32768; // laut PSDK sind 32k das Maximum
  446. begin
  447.   SetLength(Result,MAXSIZE);
  448.   SetLength(Result,ExpandEnvironmentStrings(pchar(szInput),
  449.     @Result[1],length(Result))-1); //-1 um abschließendes #0 zu verwerfen
  450. end;
  451.  
  452. // Beispiele
  453. // Papierkorb                                                 -- Windows 95
  454. // @C:\WINNT\system32\shell32.dll,-8964@1031,Papierkorb       -- Windows 2000
  455.  
  456. function _DecodeReferenceString(s: string): string;
  457. var
  458.   dll, id, lang, cache: string;
  459.   sl, sl2: tstringlist;
  460. begin
  461.   if Copy(s, 1, 1) = '@' then
  462.   begin
  463.     // Referenz auf eine DLL
  464.     // @<dll>,-<id>[@<lang>][,<cache>]
  465.  
  466.     sl := TStringList.Create;
  467.     try
  468.       // '@' am Anfang entfernen
  469.       s := Copy(s, 2, length(s)-1);
  470.  
  471.       // Nach ',' auftrennen
  472.       // sl[0] --> dll
  473.       // sl[1] --> -id@lang
  474.       // sl[2] --> cache
  475.       sl.CommaText := s;
  476.  
  477.       if sl.Count > 2 then
  478.       begin
  479.         // Das Ergebnis ist bereits im Klartext vorhanden und muss nicht extrahiert werden
  480.         // Ist bei Windows 2000 der Fall
  481.         cache := sl[2];
  482.         result := cache;
  483.         exit;
  484.       end;
  485.  
  486.       if sl.Count > 1 then
  487.       begin
  488.         dll := sl[0];
  489.  
  490.         sl2 := TStringList.Create;
  491.         try
  492.           // Nach '@' auftrennen
  493.           // sl2[0] --> id
  494.           // sl2[1] --> lang
  495.           sl2.CommaText := StringReplace(sl[1], '@', ',', [rfReplaceAll]);
  496.  
  497.           id := sl2[0];
  498.  
  499.           if sl2.Count > 1 then
  500.           begin
  501.             // ToDo: In Zukunft beachten, sofern möglich
  502.             lang := sl2[1];
  503.           end;
  504.  
  505.           // Umgebungsvariablen erkennen und Minuszeichen entfernen
  506.           result := _GetStringFromDLL(_ExpandEnvStr(dll), -StrToInt(id));
  507.         finally
  508.           sl2.Free;
  509.         end;
  510.       end
  511.       else
  512.       begin
  513.         // Zu wenige Informationen!
  514.  
  515.         result := '';
  516.       end;
  517.     finally
  518.       sl.Free;
  519.     end;
  520.   end
  521.   else
  522.   begin
  523.     // Kein Hinweis auf eine Referenz
  524.     result := s;
  525.   end;
  526. end;
  527.  
  528. function _readInt8(const Stream: TStream): byte;
  529. var
  530.   I: integer;
  531. begin
  532.   i := 0;
  533.   Stream.ReadBuffer(i, 1);
  534.   Result := i;
  535. end;
  536.  
  537. function _readInt32(const Stream: TStream): Longword;
  538. var
  539.   I: integer;
  540. begin
  541.   i := 0;
  542.   Stream.ReadBuffer(i, 4);
  543.   Result := i;
  544. end;
  545.  
  546. function _readInt64(const Stream: TStream): int64;
  547. var
  548.   I: int64;
  549. begin
  550.   i := 0;
  551.   Stream.ReadBuffer(i, 8);
  552.   Result := i;
  553. end;
  554.  
  555. function _readChar(const Stream: TStream): char;
  556. var
  557.   C: Char;
  558. begin
  559.   C := #0;
  560.   Stream.ReadBuffer(C, 1);
  561.   Result := C;
  562. end;
  563.  
  564. function _readNullTerminatedString(const Stream: TStream): String;
  565. var
  566.   S: String;
  567.   C: Char;
  568. begin
  569.   S := '';
  570.   repeat
  571.     Stream.ReadBuffer(C, 1);
  572.     if (C <> #0) then
  573.       S := S + C;
  574.   until C = #0;
  575.   Result := S;
  576. end;
  577.  
  578. // http://www.delphipraxis.net/post761928.html#761928
  579. function _readNullTerminatedWideString(const Stream: TStream): WideString;
  580. var
  581.   S: WideString;
  582.   WC: WideChar;
  583. begin
  584.   S := '';
  585.   repeat
  586.     Stream.ReadBuffer(WC, 2);
  587.     if (WC <> #0) then
  588.       S := S + WC;
  589.   until WC = #0;
  590.   Result := S;
  591. end;
  592.  
  593. // http://www.delphipraxis.net/post340194.html#340194
  594. function _nowUTC: TDateTime;
  595. var
  596.   SystemTime: TSystemTime;
  597. begin
  598.   GetSystemTime(SystemTime);
  599.   with SystemTime do
  600.   begin
  601.     Result := EncodeDate(wYear, wMonth, wDay) +
  602.               EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
  603.   end;
  604. end;
  605.  
  606. {$IFDEF DEL6UP}
  607. function _getGMTDifference(): extended;
  608. begin
  609.   result := - (datetimetounix(_nowUTC())-datetimetounix(Now())) / 3600;
  610. end;
  611.  
  612. function _fileTimeToDateTime(FileTime: int64): TDateTime;
  613. begin
  614.   // http://www.e-fense.com/helix/Docs/Recycler_Bin_Record_Reconstruction.pdf
  615.   // UnixTime = 0.0000001 * NTTime + 11644473600
  616.   // This is wrong! The correct formula is:
  617.   // UnixTime = 0.0000001 * NTTime - 11644473600 + c * 3600
  618.   // c = GMT-Difference (MEZ = 1) inclusive daylight saving time (+3600 seconds)
  619.   result := unixtodatetime(FileTime div 10000000 - 11644473600 + round(_getGMTDifference() * 3600));
  620. end;
  621. {$ENDIF}
  622.  
  623. // http://www.delphipraxis.net/post471470.html
  624. function _getAccountSid(const Server, User: WideString; var Sid: PSID): DWORD;
  625. var
  626.   dwDomainSize, dwSidSize: DWord;
  627.   R: LongBool;
  628.   wDomain: WideString;
  629.   Use: DWord;
  630. begin
  631.   Result := 0;
  632.   SetLastError(0);
  633.   dwSidSize := 0;
  634.   dwDomainSize := 0;
  635.   R := LookupAccountNameW(PWideChar(Server), PWideChar(User), nil, dwSidSize,
  636.        nil, dwDomainSize, Use);
  637.   if (not R) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
  638.   begin
  639.     SetLength(wDomain, dwDomainSize);
  640.     Sid := GetMemory(dwSidSize);
  641.     R := LookupAccountNameW(PWideChar(Server), PWideChar(User), Sid,
  642.          dwSidSize, PWideChar(wDomain), dwDomainSize, Use);
  643.     if not R then
  644.     begin
  645.       FreeMemory(Sid);
  646.       Sid := nil;
  647.     end;
  648.   end
  649.   else
  650.     Result := GetLastError;
  651. end;
  652.  
  653. // Template:
  654. // http://www.latiumsoftware.com/en/pascal/0014.php
  655. function _getLoginNameW: widestring;
  656. var
  657.   Buffer: array[0..255] of widechar;
  658.   Size: dword;
  659. begin
  660.   Size := 256;
  661.   if GetUserNameW(Buffer, Size) then
  662.     Result := Buffer
  663.   else
  664.     Result := 'User';
  665. end;
  666.  
  667. function _Dyn_ConvertSidToStringSidA(SID: PSID; var strSID: LPSTR): boolean;
  668. type
  669.   DllReg = function(SID: PSID; var StringSid: LPSTR): Boolean; stdcall;
  670. var
  671.   hDll: THandle;
  672.   dr: DllReg;
  673. begin
  674.   result := false;
  675.   hDll := LoadLibrary(advapi32);
  676.   if hDll <> 0 then
  677.   begin
  678.     @dr := GetProcAddress(hDll, 'ConvertSidToStringSidA');
  679.     if assigned(dr) then
  680.     begin
  681.       result := dr(SID, strSID);
  682.     end;
  683.   end;
  684. end;
  685.  
  686. // http://www.delphipraxis.net/post471470.html
  687. // Changed
  688. function _getMySID(): string;
  689. var
  690.   SID: PSID;
  691.   strSID: PAnsiChar;
  692.   s: String;
  693.   err: DWORD;
  694. begin
  695.   SID := nil;
  696.  
  697.   err := _getAccountSid('', _getLoginNameW(), SID);
  698.  
  699.   if err = 0 then
  700.   begin
  701.     if _Dyn_ConvertSidToStringSidA(SID, strSID) then
  702.       s := string(strSID)
  703.     else
  704.       s := SysErrorMessage(err);
  705.   end
  706.   else
  707.     s := SysErrorMessage(err);
  708.  
  709.   result := s;
  710. end;
  711.  
  712. // Originalcode aus http://www.delphipraxis.net/post2933.html
  713. function _DriveExists(DriveByte: Byte): Boolean; overload;
  714. begin
  715.   Result := GetLogicalDrives and (1 shl DriveByte) <> 0;
  716. end;
  717.  
  718. function _driveExists(Drive: Char): Boolean; overload;
  719. var
  720.   DriveByte: Byte;
  721.   tmp: string;
  722. begin
  723.   // Make drive letter upper case (for older Delphi versions)
  724.   tmp := UpperCase(Drive);
  725.   Drive := tmp[1];
  726.  
  727.   DriveByte := Ord(Drive) - Ord('A');
  728.   Result := _DriveExists(DriveByte);
  729. end;
  730.  
  731. function _isFAT(drive: char): boolean;
  732. var
  733.   Dummy2: DWORD;
  734.   Dummy3: DWORD;
  735.   FileSystem: array[0..MAX_PATH] of char;
  736.   VolumeName: array[0..MAX_PATH] of char;
  737.   s: string;
  738. begin
  739.   result := false;
  740.   if _driveExists(drive) then
  741.   begin
  742.     s := drive + DriveDelim + PathDelim; // ohne die Auslagerung in einen String kommt es zu einer AV in ntdll
  743.     GetVolumeInformation(PChar(s), VolumeName,
  744.       SizeOf(VolumeName), nil, Dummy2, Dummy3, FileSystem, SizeOf(FileSystem));
  745.     result := uppercase(copy(FileSystem, 0, 3)) = 'FAT';
  746.   end;
  747. end;
  748.  
  749. // http://www.delphipages.com/tips/thread.cfm?ID=294
  750. // Changed
  751. (* function IsWin95: boolean;
  752. var
  753.   OS: TOSVersionInfo;
  754. begin
  755.   ZeroMemory(@OS, SizeOf(OS));
  756.   OS.dwOSVersionInfoSize := SizeOf(OS);
  757.   GetVersionEx(OS);
  758.   // http://www.gaijin.at/lstwinver.php
  759.   Result := (OS.dwMajorVersion = 4) and (OS.dwMinorVersion <= 3) and
  760.             (OS.dwPlatformId=VER_PLATFORM_WIN32_WINDOWS);
  761. end; *)
  762.  
  763. // http://www.delphipages.com/tips/thread.cfm?ID=294
  764. // Changed
  765. function _isVista: boolean;
  766. var
  767.   OS: TOSVersionInfo;
  768. begin
  769.   ZeroMemory(@OS, SizeOf(OS));
  770.   OS.dwOSVersionInfoSize := SizeOf(OS);
  771.   GetVersionEx(OS);
  772.   // http://www.gaijin.at/lstwinver.php
  773.   Result := (OS.dwMajorVersion = 6) and (OS.dwMinorVersion = 0) and
  774.             (OS.dwPlatformId=VER_PLATFORM_WIN32_NT);
  775. end;
  776.  
  777. // **********************************************************
  778. // VISTA FUNCTIONS, INTERNAL USED
  779. // **********************************************************
  780.  
  781. function _isFileVistaRealfile(filename: string): boolean;
  782. begin
  783.   result := uppercase(copy(extractfilename(filename), 0, 2)) = '$R';
  784. end;
  785.  
  786. function _isFileVistaIndexfile(filename: string): boolean;
  787. begin
  788.   result := uppercase(copy(extractfilename(filename), 0, 2)) = '$I';
  789. end;
  790.  
  791. function _isFileVistaNamed(filename: string): boolean;
  792. begin
  793.   result := _isFileVistaIndexfile(filename) or
  794.             _isFileVistaRealfile(filename);
  795. end;
  796.  
  797. function _VistaChangeRealfileToIndexfile(realfile: string): string;
  798. begin
  799.   if _isFileVistaRealfile(realfile) then
  800.   begin
  801.     result := extractfilepath(realfile)+'$I'+
  802.       copy(extractfilename(realfile), 3, length(extractfilename(realfile))-2);
  803.   end
  804.   else
  805.     result := realfile; // ignore, even if it is not a vista recycle-file
  806. end;
  807.  
  808. function _VistaChangeIndexfileToRealfile(indexfile: string): string;
  809. begin
  810.   if _isFileVistaIndexfile(indexfile) then
  811.   begin
  812.     result := extractfilepath(indexfile)+'$R'+
  813.       copy(extractfilename(indexfile), 3, length(extractfilename(indexfile))-2);
  814.   end
  815.   else
  816.     result := indexfile; // ignore, even if it is not a vista recycle-file
  817. end;
  818.  
  819. procedure _VistaListIndexes(recyclerpath: string; result: TStringList);
  820. var
  821.   sr: TSearchRec;
  822.   r: Integer;
  823.   tmp: string;
  824. begin
  825.   tmp := recyclerpath;
  826.   tmp := IncludeTrailingBackslash(tmp);
  827.  
  828.   if not directoryexists(tmp) then exit;
  829.  
  830.   r := FindFirst(tmp+PathDelim + '$I*', faAnyFile, sr);
  831.   while r = 0 do
  832.   begin
  833.     if (sr.Name <> '.') and (sr.Name <> '..') then
  834.     begin
  835.       if sr.Size = $220 then
  836.       begin
  837.         result.Add(copy(sr.name, 3, length(sr.name)-2));
  838.       end;
  839.     end;
  840.     r := FindNext(sr);
  841.   end;
  842.  
  843.   FindClose(sr);
  844. end;
  845.  
  846. function _VistaCurrentFilename(infofilename: string): string;
  847. begin
  848.   result := extractfilename(infofilename);
  849.  
  850.   if _isFileVistaRealfile(result) then
  851.   begin
  852.     exit;
  853.   end;
  854.  
  855.   if _isFileVistaIndexfile(result) then
  856.   begin
  857.     result := _VistaChangeIndexfileToRealfile(result);
  858.     exit;
  859.   end;
  860.  
  861.   result := copy(result, 3, length(result)-2);
  862.   result := '$R'+result;
  863. end;
  864.  
  865. function _VistaGetSourceDrive(infofile: string): char;
  866. var
  867.   fs: TFileStream;
  868.   tmp: string;
  869. const
  870.   drive_vista_position = $18;
  871. begin
  872.   result := #0;
  873.  
  874.   tmp := infofile;
  875.   tmp := _VistaChangeRealfileToIndexfile(tmp);
  876.   if not fileexists(tmp) then exit;
  877.  
  878.   fs := TFileStream.Create(tmp, fmOpenRead);
  879.   try
  880.     fs.seek(drive_vista_position, soFromBeginning);
  881.     result := _readChar(fs);
  882.   finally
  883.     fs.free;
  884.   end;
  885. end;
  886.  
  887. {$IFDEF DEL6UP}
  888. function _VistaGetDateTime(infofile: string): TDateTime;
  889. var
  890.   fs: TFileStream;
  891.   tmp: string;
  892. const
  893.   timestamp_vista_position = $10;
  894. begin
  895.   result := EncodeDateTime(1601, 1, 1, 0, 0, 0, 0);
  896.  
  897.   tmp := infofile;
  898.   tmp := _VistaChangeRealfileToIndexfile(tmp);
  899.   if not fileexists(tmp) then exit;
  900.  
  901.   fs := TFileStream.Create(tmp, fmOpenRead);
  902.   try
  903.     fs.seek(timestamp_vista_position, soFromBeginning);
  904.     result := _fileTimeToDateTime(_readInt64(fs));
  905.   finally
  906.     fs.free;
  907.   end;
  908. end;
  909. {$ENDIF}
  910.  
  911. function _VistaGetSourceUnicode(infofile: string): string;
  912. var
  913.   fs: TFileStream;
  914.   tmp: string;
  915. const
  916.   unicode_vista_position = $18;
  917. begin
  918.   result := '';
  919.  
  920.   tmp := infofile;
  921.   tmp := _VistaChangeRealfileToIndexfile(tmp);
  922.   if not fileexists(tmp) then exit;
  923.  
  924.   fs := TFileStream.Create(tmp, fmOpenRead);
  925.   try
  926.     fs.seek(unicode_vista_position, soFromBeginning);
  927.     result := _readNullTerminatedWideString(fs);
  928.   finally
  929.     fs.free;
  930.   end;
  931. end;
  932.  
  933. function _VistaOriginalSize(infofile: string): integer;
  934. var
  935.   fs: TFileStream;
  936.   tmp: string;
  937. const
  938.   size_vista_position = $8;
  939. begin
  940.   result := -1;
  941.  
  942.   tmp := infofile;
  943.   tmp := _VistaChangeRealfileToIndexfile(tmp);
  944.   if not fileexists(tmp) then exit;
  945.  
  946.   fs := TFileStream.Create(tmp, fmOpenRead);
  947.   try
  948.     fs.seek(size_vista_position, soFromBeginning);
  949.     result := _readInt32(fs);
  950.   finally
  951.     fs.free;
  952.   end;
  953. end;
  954.  
  955. function _checkInfo1or2File(filename: string): boolean;
  956. var
  957.   fs: TStream;
  958.   record_length: integer;
  959. const
  960.   length_position = $C;
  961.   empty_size = 20;
  962. begin
  963.   fs := TFileStream.Create(filename, fmOpenRead);
  964.   try
  965.     fs.seek(length_position, soFromBeginning);
  966.     record_length := _readInt32(fs);
  967.  
  968.     // Check the file length
  969.     if record_length = 0 then
  970.       result := false
  971.     else
  972.       result := (fs.size - empty_size) mod record_length = 0;
  973.   finally
  974.     fs.free;
  975.   end;
  976. end;
  977.  
  978. function _VistaIsValid(infofile: string): boolean;
  979. var
  980.   fs: TFileStream;
  981.   tmp: string;
  982. const
  983.   vista_valid_size = $220;
  984. begin
  985.   result := false;
  986.  
  987.   tmp := infofile;
  988.   tmp := _VistaChangeRealfileToIndexfile(tmp);
  989.   if not fileexists(tmp) then exit;
  990.  
  991.   fs := TFileStream.Create(tmp, fmOpenRead);
  992.   try
  993.     // Check the file length
  994.     result := fs.size = vista_valid_size;
  995.   finally
  996.     fs.free;
  997.   end;
  998. end;
  999.  
  1000. // **********************************************************
  1001. // PUBLIC FUNCTIONS
  1002. // **********************************************************
  1003.  
  1004. {$IFDEF DEL6UP}
  1005.  
  1006. function RecyclerGetDateTime(InfofileOrRecycleFolder: string): tdatetime; overload;
  1007. begin
  1008.   result := RecyclerGetDateTime(InfofileOrRecycleFolder, '');
  1009. end;
  1010.  
  1011. function RecyclerGetDateTime(drive: char; fileid: string): tdatetime; overload;
  1012. begin
  1013.   result := RecyclerGetDateTime(drive, '', fileid);
  1014. end;
  1015.  
  1016. function RecyclerGetDateTime(drive: char; UserSID: string; fileid: string): tdatetime; overload;
  1017. var
  1018.   infofile: string;
  1019. begin
  1020.   infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1021.   result := RecyclerGetDateTime(infofile, fileid);
  1022. end;
  1023.  
  1024. function RecyclerGetDateTime(InfofileOrRecycleFolder: string; id: string): tdatetime; overload;
  1025. var
  1026.   fs: TFileStream;
  1027.   i, record_length: integer;
  1028.   tmp: string;
  1029. const
  1030.   length_position = $C;
  1031.   unique_index_position = $118;
  1032.   timestamp_position = $120;
  1033. begin
  1034.   // FILETIME does start at 01.01.1601 00:00:00 (GMT)
  1035.   result := EncodeDateTime(1601, 1, 1, 0, 0, 0, 0);
  1036.  
  1037.   tmp := InfofileOrRecycleFolder;
  1038.  
  1039.   if _isFileVistaNamed(tmp) then
  1040.   begin
  1041.     result := _VistaGetDateTime(tmp);
  1042.     exit;
  1043.   end;
  1044.  
  1045.   {$IFDEF allow_all_filenames}
  1046.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1047.   begin
  1048.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1049.       tmp := extractfilepath(tmp)+'INFO2'
  1050.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1051.       tmp := extractfilepath(tmp)+'INFO';
  1052.   end;
  1053.   {$ENDIF}
  1054.  
  1055.   if directoryexists(tmp) then
  1056.   begin
  1057.     tmp := IncludeTrailingBackslash(tmp);
  1058.  
  1059.     if fileexists(tmp+'$I'+id) then
  1060.     begin
  1061.       result := _VistaGetDateTime(tmp+'$I'+id);
  1062.       exit;
  1063.     end
  1064.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1065.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1066.   end;
  1067.  
  1068.   if not fileexists(tmp) then exit;
  1069.   if not RecyclerIsValid(tmp) then exit;
  1070.  
  1071.   fs := TFileStream.Create(tmp, fmOpenRead);
  1072.   try
  1073.     fs.seek(length_position, soFromBeginning);
  1074.     record_length := _readInt32(fs);
  1075.  
  1076.     i := -1;
  1077.     repeat
  1078.       inc(i);
  1079.       if unique_index_position+i*record_length > fs.size then break;
  1080.       fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1081.       if inttostr(_readInt32(fs)) = id then
  1082.       begin
  1083.         fs.seek(timestamp_position+i*record_length, soFromBeginning);
  1084.         result := _fileTimeToDateTime(_readInt64(fs));
  1085.         break;
  1086.       end;
  1087.       until false;
  1088.   finally
  1089.     fs.free;
  1090.   end;
  1091. end;
  1092.  
  1093. {$ENDIF}
  1094.  
  1095. ////////////////////////////////////////////////////////////////////////////////
  1096.  
  1097. function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string): WideString; overload;
  1098. begin
  1099.   result := RecyclerGetSourceUnicode(InfofileOrRecycleFolder, '');
  1100. end;
  1101.  
  1102. function RecyclerGetSourceUnicode(drive: char; fileid: string): WideString; overload;
  1103. begin
  1104.   result := RecyclerGetSourceUnicode(drive, '', fileid);
  1105. end;
  1106.  
  1107. function RecyclerGetSourceUnicode(drive: char; UserSID: string; fileid: string): WideString; overload;
  1108. var
  1109.   infofile: string;
  1110. begin
  1111.   if Win32Platform = VER_PLATFORM_WIN32_NT then
  1112.   begin
  1113.     infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1114.     result := RecyclerGetSourceUnicode(infofile, fileid);
  1115.   end
  1116.   else
  1117.   begin
  1118.     // Windows 9x does not support unicode
  1119.     result := RecyclerGetSource(drive, UserSID, fileid);
  1120.   end;
  1121. end;
  1122.  
  1123. function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string; id: string): WideString; overload;
  1124. var
  1125.   fs: TFileStream;
  1126.   i, record_length: integer;
  1127.   tmp: string;
  1128. const
  1129.   length_position = $C;
  1130.   unique_index_position = $118;
  1131.   unicode_source_position = $12C;
  1132. begin
  1133.   result := '';
  1134.  
  1135.   tmp := InfofileOrRecycleFolder;
  1136.  
  1137.   if _isFileVistaNamed(tmp) then
  1138.   begin
  1139.     // Vista only gives unicode names
  1140.     result := _VistaGetSourceUnicode(tmp);
  1141.     exit;
  1142.   end;
  1143.  
  1144.   {$IFDEF allow_all_filenames}
  1145.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1146.   begin
  1147.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1148.       tmp := extractfilepath(tmp)+'INFO2'
  1149.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1150.       tmp := extractfilepath(tmp)+'INFO';
  1151.   end;
  1152.   {$ENDIF}
  1153.  
  1154.   if directoryexists(tmp) then
  1155.   begin
  1156.     tmp := IncludeTrailingBackslash(tmp);
  1157.  
  1158.     if fileexists(tmp+'$I'+id) then
  1159.     begin
  1160.       // Vista only gives unicode names
  1161.       result := _VistaGetSourceUnicode(tmp+'$I'+id);
  1162.       exit;
  1163.     end
  1164.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1165.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1166.   end;
  1167.  
  1168.   if not fileexists(tmp) then exit;
  1169.   if not RecyclerIsValid(tmp) then exit;
  1170.  
  1171.   fs := TFileStream.Create(tmp, fmOpenRead);
  1172.   try
  1173.     fs.seek(length_position, soFromBeginning);
  1174.     record_length := _readInt32(fs);
  1175.  
  1176.     if record_length <> $118 then
  1177.     begin
  1178.       // Windows NT
  1179.       i := -1;
  1180.       repeat
  1181.         inc(i);
  1182.         if unique_index_position+i*record_length > fs.size then break;
  1183.         fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1184.         if inttostr(_readInt32(fs)) = id then
  1185.         begin
  1186.           fs.seek(unicode_source_position+i*record_length, soFromBeginning);
  1187.           result := _readNullTerminatedWideString(fs);
  1188.           break;
  1189.         end;
  1190.       until false;
  1191.     end;
  1192.   finally
  1193.     fs.free;
  1194.   end;
  1195.  
  1196.   if record_length = $118 then
  1197.   begin
  1198.     // Windows 9x has no unicode support
  1199.     result := RecyclerGetSource(tmp, id);
  1200.   end;
  1201. end;
  1202.  
  1203. ////////////////////////////////////////////////////////////////////////////////
  1204.  
  1205. function RecyclerGetSource(InfofileOrRecycleFolder: string): string; overload;
  1206. begin
  1207.   result := RecyclerGetSource(InfofileOrRecycleFolder, '');
  1208. end;
  1209.  
  1210. function RecyclerGetSource(drive: char; fileid: string): string; overload;
  1211. begin
  1212.   result := RecyclerGetSource(drive, '', fileid);
  1213. end;
  1214.  
  1215. function RecyclerGetSource(drive: char; UserSID: string; fileid: string): string; overload;
  1216. var
  1217.   infofile: string;
  1218. begin
  1219.   infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1220.   result := RecyclerGetSource(infofile, fileid);
  1221. end;
  1222.  
  1223. function RecyclerGetSource(InfofileOrRecycleFolder: string; id: string): string; overload;
  1224. var
  1225.   fs: TFileStream;
  1226.   i, record_length: integer;
  1227.   tmp: string;
  1228.   alternativ: string;
  1229. const
  1230.   length_position = $C;
  1231.   unique_index_position = $118;
  1232.   source_position = $14;
  1233. begin
  1234.   result := '';
  1235.  
  1236.   tmp := InfofileOrRecycleFolder;
  1237.  
  1238.   if _isFileVistaNamed(tmp) then
  1239.   begin
  1240.     // Vista only gives unicode names
  1241.     result := _VistaGetSourceUnicode(tmp);
  1242.     exit;
  1243.   end;
  1244.  
  1245.   {$IFDEF allow_all_filenames}
  1246.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1247.   begin
  1248.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1249.       tmp := extractfilepath(tmp)+'INFO2'
  1250.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1251.       tmp := extractfilepath(tmp)+'INFO';
  1252.   end;
  1253.   {$ENDIF}
  1254.  
  1255.   if directoryexists(tmp) then
  1256.   begin
  1257.     tmp := IncludeTrailingBackslash(tmp);
  1258.  
  1259.     if fileexists(tmp+'$I'+id) then
  1260.     begin
  1261.       // Vista only gives unicode names
  1262.       result := _VistaGetSourceUnicode(tmp+'$I'+id);
  1263.       exit;
  1264.     end
  1265.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1266.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1267.   end;
  1268.  
  1269.   if not fileexists(tmp) then exit;
  1270.   if not RecyclerIsValid(tmp) then exit;
  1271.  
  1272.   fs := TFileStream.Create(tmp, fmOpenRead);
  1273.   try
  1274.     fs.seek(length_position, soFromBeginning);
  1275.     record_length := _readInt32(fs);
  1276.  
  1277.     i := -1;
  1278.     repeat
  1279.       inc(i);
  1280.       if unique_index_position+i*record_length > fs.size then break;
  1281.       fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1282.       if inttostr(_readInt32(fs)) = id then
  1283.       begin
  1284.         fs.seek(source_position+i*record_length, soFromBeginning);
  1285.         alternativ := _readChar(fs);
  1286.  
  1287.         if alternativ = #0 then
  1288.         begin
  1289.           fs.seek(source_position+i*record_length+1, soFromBeginning);
  1290.           result := _readNullTerminatedString(fs);
  1291.         end
  1292.         else
  1293.         begin
  1294.           fs.seek(source_position+i*record_length, soFromBeginning);
  1295.           result := _readNullTerminatedString(fs);
  1296.         end;
  1297.  
  1298.         break;
  1299.       end;
  1300.     until false;
  1301.   finally
  1302.     fs.free;
  1303.   end;
  1304.  
  1305.   // In some cases the ansi-source-name is [Null]:\...\
  1306.   if alternativ = #0 then
  1307.   begin
  1308.     result := RecyclerGetSourceDrive(InfofileOrRecycleFolder, id) + result;
  1309.   end;
  1310. end;
  1311.  
  1312. ////////////////////////////////////////////////////////////////////////////////
  1313.  
  1314. procedure RecyclerListIndexes(drive: char; result: TStringList); overload;
  1315. begin
  1316.   RecyclerListIndexes(drive, '', result);
  1317. end;
  1318.  
  1319. procedure RecyclerListIndexes(drive: char; UserSID: string; result: TStringList); overload;
  1320. var
  1321.   infofile: string;
  1322. begin
  1323.   infofile := RecyclerGetPath(drive, UserSID, false);
  1324.   RecyclerListIndexes(infofile, result);
  1325. end;
  1326.  
  1327. procedure RecyclerListIndexes(InfofileOrRecycleFolder: string; result: TStringList); overload;
  1328. var
  1329.   fs: TFileStream;
  1330.   i, record_length: integer;
  1331.   tmp: string;
  1332. const
  1333.   length_position = $C;
  1334.   unique_index_position = $118;
  1335. begin
  1336.   tmp := InfofileOrRecycleFolder;
  1337.  
  1338.   if _isFileVistaNamed(tmp) then
  1339.   begin
  1340.     _VistaListIndexes(extractfilepath(tmp), result);
  1341.     exit;
  1342.   end;
  1343.  
  1344.   {$IFDEF allow_all_filenames}
  1345.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1346.   begin
  1347.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1348.       tmp := extractfilepath(tmp)+'INFO2'
  1349.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1350.       tmp := extractfilepath(tmp)+'INFO';
  1351.   end;
  1352.   {$ENDIF}
  1353.  
  1354.   if directoryexists(tmp) then
  1355.   begin
  1356.     tmp := IncludeTrailingBackslash(tmp);
  1357.  
  1358.     if fileexists(tmp+'INFO2') then     tmp := tmp+'INFO2'
  1359.     else if fileexists(tmp+'INFO') then tmp := tmp+'INFO'
  1360.     else
  1361.     begin
  1362.       // Last try: is it a vista-directory?
  1363.       _VistaListIndexes(tmp, result);
  1364.       exit;
  1365.     end;
  1366.   end;
  1367.  
  1368.   if not fileexists(tmp) then exit;
  1369.   if not RecyclerIsValid(tmp) then exit;
  1370.  
  1371.   fs := TFileStream.Create(tmp, fmOpenRead);
  1372.   try
  1373.     fs.seek(length_position, soFromBeginning);
  1374.     record_length := _readInt32(fs);
  1375.  
  1376.     i := -1;
  1377.     repeat
  1378.       inc(i);
  1379.       if unique_index_position+i*record_length > fs.size then break;
  1380.       fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1381.  
  1382.       result.Add(inttostr(_readInt32(fs)));
  1383.     until false;
  1384.   finally
  1385.     fs.free;
  1386.   end;
  1387. end;
  1388.  
  1389. ////////////////////////////////////////////////////////////////////////////////
  1390.  
  1391. function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string): char; overload;
  1392. begin
  1393.   result := RecyclerGetSourceDrive(InfofileOrRecycleFolder, '');
  1394. end;
  1395.  
  1396. function RecyclerGetSourceDrive(drive: char; fileid: string): char; overload;
  1397. begin
  1398.   result := RecyclerGetSourceDrive(drive, '', fileid);
  1399. end;
  1400.  
  1401. function RecyclerGetSourceDrive(drive: char; UserSID: string; fileid: string): char; overload;
  1402. var
  1403.   infofile: string;
  1404. begin
  1405.   infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1406.   result := RecyclerGetSourceDrive(infofile, fileid);
  1407. end;
  1408.  
  1409. function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string; id: string): char; overload;
  1410. var
  1411.   fs: TFileStream;
  1412.   i, record_length: integer;
  1413.   tmp: string;
  1414. const
  1415.   length_position = $C;
  1416.   unique_index_position = $118;
  1417.   source_drive_position = $11C;
  1418. begin
  1419.   result := #0;
  1420.  
  1421.   tmp := InfofileOrRecycleFolder;
  1422.  
  1423.   if _isFileVistaNamed(tmp) then
  1424.   begin
  1425.     result := _VistaGetSourceDrive(tmp);
  1426.     exit;
  1427.   end;
  1428.  
  1429.   {$IFDEF allow_all_filenames}
  1430.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1431.   begin
  1432.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1433.       tmp := extractfilepath(tmp)+'INFO2'
  1434.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1435.       tmp := extractfilepath(tmp)+'INFO';
  1436.   end;
  1437.   {$ENDIF}
  1438.  
  1439.   if directoryexists(tmp) then
  1440.   begin
  1441.     tmp := IncludeTrailingBackslash(tmp);
  1442.  
  1443.     if fileexists(tmp+'$I'+id) then
  1444.     begin
  1445.       result := _VistaGetSourceDrive(tmp+'$I'+id);
  1446.       exit;
  1447.     end
  1448.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1449.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1450.   end;
  1451.  
  1452.   if not fileexists(tmp) then exit;
  1453.   if not RecyclerIsValid(tmp) then exit;
  1454.  
  1455.   fs := TFileStream.Create(tmp, fmOpenRead);
  1456.   try
  1457.     fs.seek(length_position, soFromBeginning);
  1458.     record_length := _readInt32(fs);
  1459.  
  1460.     i := -1;
  1461.     repeat
  1462.       inc(i);
  1463.       if unique_index_position+i*record_length > fs.size then break;
  1464.       fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1465.       if inttostr(_readInt32(fs)) = id then
  1466.       begin
  1467.         fs.seek(source_drive_position+i*record_length, soFromBeginning);
  1468.         result := chr(ord('A') + _readInt8(fs));
  1469.         break;
  1470.       end;
  1471.     until false;
  1472.   finally
  1473.     fs.free;
  1474.   end;
  1475. end;
  1476.  
  1477. ////////////////////////////////////////////////////////////////////////////////
  1478.  
  1479. function RecyclerOriginalSize(InfofileOrRecycleFolder: string): integer; overload;
  1480. begin
  1481.   result := RecyclerOriginalSize(InfofileOrRecycleFolder, '');
  1482. end;
  1483.  
  1484. function RecyclerOriginalSize(drive: char; fileid: string): integer; overload;
  1485. begin
  1486.   result := RecyclerOriginalSize(drive, '', fileid);
  1487. end;
  1488.  
  1489. function RecyclerOriginalSize(drive: char; UserSID: string; fileid: string): integer; overload;
  1490. var
  1491.   infofile: string;
  1492. begin
  1493.   infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1494.   result := RecyclerOriginalSize(infofile, fileid);
  1495. end;
  1496.  
  1497. function RecyclerOriginalSize(InfofileOrRecycleFolder: string; id: string): integer; overload;
  1498. var
  1499.   fs: TFileStream;
  1500.   i, record_length: integer;
  1501.   tmp: string;
  1502. const
  1503.   length_position = $C;
  1504.   unique_index_position = $118;
  1505.   original_size_position = $128;
  1506. begin
  1507.   result := -1;
  1508.  
  1509.   tmp := InfofileOrRecycleFolder;
  1510.  
  1511.   if _isFileVistaNamed(tmp) then
  1512.   begin
  1513.     result := _VistaOriginalSize(tmp);
  1514.     exit;
  1515.   end;
  1516.  
  1517.   {$IFDEF allow_all_filenames}
  1518.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1519.   begin
  1520.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1521.       tmp := extractfilepath(tmp)+'INFO2'
  1522.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1523.       tmp := extractfilepath(tmp)+'INFO';
  1524.   end;
  1525.   {$ENDIF}
  1526.  
  1527.   if directoryexists(tmp) then
  1528.   begin
  1529.     tmp := IncludeTrailingBackslash(tmp);
  1530.  
  1531.     if fileexists(tmp+'$I'+id) then
  1532.     begin
  1533.       result := _VistaOriginalSize(tmp+'$I'+id);
  1534.       exit;
  1535.     end
  1536.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1537.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1538.   end;
  1539.  
  1540.   if not fileexists(tmp) then exit;
  1541.   if not RecyclerIsValid(tmp) then exit;
  1542.  
  1543.   fs := TFileStream.Create(tmp, fmOpenRead);
  1544.   try
  1545.     fs.seek(length_position, soFromBeginning);
  1546.     record_length := _readInt32(fs);
  1547.  
  1548.     i := -1;
  1549.     repeat
  1550.       inc(i);
  1551.       if unique_index_position+i*record_length > fs.size then break;
  1552.       fs.seek(unique_index_position+i*record_length, soFromBeginning);
  1553.       if inttostr(_readInt32(fs)) = id then
  1554.       begin
  1555.         fs.seek(original_size_position+i*record_length, soFromBeginning);
  1556.         result := _readInt32(fs);
  1557.         break;
  1558.       end;
  1559.     until false;
  1560.   finally
  1561.     fs.free;
  1562.   end;
  1563. end;
  1564.  
  1565. ////////////////////////////////////////////////////////////////////////////////
  1566.  
  1567. function RecyclerIsValid(drive: char): boolean; overload;
  1568. begin
  1569.   result := RecyclerIsValid(drive, '');
  1570. end;
  1571.  
  1572. function RecyclerIsValid(drive: char; UserSID: string): boolean; overload;
  1573. var
  1574.   infofile: string;
  1575. begin
  1576.   infofile := RecyclerGetPath(drive, UserSID, false);
  1577.   result := RecyclerIsValid(infofile);
  1578. end;
  1579.  
  1580. function RecyclerIsValid(InfofileOrRecycleFolder: string): boolean; overload;
  1581. var
  1582.   tmp: string;
  1583.   x: TStringList;
  1584.   i: integer;
  1585.   eine_fehlerhaft: boolean;
  1586. begin
  1587.   result := false;
  1588.  
  1589.   tmp := InfofileOrRecycleFolder;
  1590.  
  1591.   if _isFileVistaNamed(tmp) then
  1592.   begin
  1593.     result := _VistaIsValid(tmp);
  1594.     exit;
  1595.   end;
  1596.  
  1597.   {$IFDEF allow_all_filenames}
  1598.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1599.   begin
  1600.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1601.       tmp := extractfilepath(tmp)+'INFO2'
  1602.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1603.       tmp := extractfilepath(tmp)+'INFO';
  1604.   end;
  1605.   {$ENDIF}
  1606.  
  1607.   if directoryexists(tmp) then
  1608.   begin
  1609.     tmp := IncludeTrailingBackslash(tmp);
  1610.  
  1611.     if fileexists(tmp+'INFO2') then
  1612.     begin
  1613.       result := _checkInfo1or2File(tmp+'INFO2');
  1614.     end;
  1615.  
  1616.     if not result and fileexists(tmp+'INFO') then
  1617.     begin
  1618.       result := _checkInfo1or2File(tmp+'INFO');
  1619.     end;
  1620.  
  1621.     if not result then
  1622.     begin
  1623.       // Complete vista-directory declared?
  1624.       eine_fehlerhaft := false;
  1625.       x := TStringList.Create;
  1626.       try
  1627.         _VistaListIndexes(tmp, x);
  1628.         for i := 0 to x.Count - 1 do
  1629.         begin
  1630.           if not _VistaIsValid(tmp+'$I'+x.Strings[i]) then
  1631.           begin
  1632.             eine_fehlerhaft := true;
  1633.           end;
  1634.         end;
  1635.       finally
  1636.         x.Free;
  1637.       end;
  1638.       result := not eine_fehlerhaft;
  1639.     end;
  1640.   end;
  1641.  
  1642.   if not fileexists(tmp) then exit;
  1643.  
  1644.   result := _checkInfo1or2File(tmp);
  1645. end;
  1646.  
  1647. ////////////////////////////////////////////////////////////////////////////////
  1648.  
  1649. function RecyclerCurrentFilename(InfofileOrRecycleFolder: string): string; overload;
  1650. begin
  1651.   result := RecyclerCurrentFilename(InfofileOrRecycleFolder, '');
  1652. end;
  1653.  
  1654. function RecyclerCurrentFilename(drive: char; fileid: string): string; overload;
  1655. begin
  1656.   result := RecyclerCurrentFilename(drive, '', fileid);
  1657. end;
  1658.  
  1659. function RecyclerCurrentFilename(drive: char; UserSID: string; fileid: string): string; overload;
  1660. var
  1661.   infofile: string;
  1662. begin                                                            
  1663.   infofile := RecyclerGetPath(drive, UserSID, true, fileid);
  1664.   result := RecyclerCurrentFilename(infofile, fileid);
  1665. end;
  1666.  
  1667. function RecyclerCurrentFilename(InfofileOrRecycleFolder: string; id: string): string; overload;
  1668. var
  1669.   a, c: string;
  1670.   tmp: string;
  1671. begin
  1672.   result := '';
  1673.  
  1674.   tmp := InfofileOrRecycleFolder;
  1675.  
  1676.   if _isFileVistaNamed(tmp) then
  1677.   begin
  1678.     result := _VistaCurrentFilename(tmp);
  1679.     exit;
  1680.   end;
  1681.  
  1682.   {$IFDEF allow_all_filenames}
  1683.   if not RecyclerIsValid(tmp) and fileexists(tmp) then
  1684.   begin
  1685.     if fileexists(extractfilepath(tmp)+'INFO2') then
  1686.       tmp := extractfilepath(tmp)+'INFO2'
  1687.     else if fileexists(extractfilepath(tmp)+'INFO') then
  1688.       tmp := extractfilepath(tmp)+'INFO';
  1689.   end;
  1690.   {$ENDIF}
  1691.  
  1692.   if directoryexists(tmp) then
  1693.   begin
  1694.     tmp := IncludeTrailingBackslash(tmp);
  1695.  
  1696.     if fileexists(tmp+'$I'+id) then
  1697.     begin
  1698.       result := _VistaCurrentFilename(tmp+'$I'+id);
  1699.       exit;
  1700.     end
  1701.     else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
  1702.     else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
  1703.   end;
  1704.  
  1705.   a := RecyclerGetSourceDrive(tmp, id);
  1706.   c := extractfileext(RecyclerGetSourceUnicode(tmp, id));
  1707.   if (a <> '') then
  1708.   begin
  1709.     result := 'D' + a + id + c;
  1710.   end;
  1711. end;
  1712.  
  1713. ////////////////////////////////////////////////////////////////////////////////
  1714.  
  1715. function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string): string; overload;
  1716. var
  1717.   sl: TStringList;
  1718. begin
  1719.   sl := TStringList.Create;
  1720.   try
  1721.     RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, fileid, sl);
  1722.     if sl.Count > 0 then
  1723.       result := ExtractFilePath(sl.Strings[0])
  1724.     else
  1725.       result := '';
  1726.   finally
  1727.     sl.free;
  1728.   end;
  1729. end;
  1730.  
  1731. function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean): string; overload;
  1732. var
  1733.   sl: TStringList;
  1734. begin
  1735.   sl := TStringList.Create;
  1736.   try
  1737.     RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, sl);
  1738.     if sl.Count > 0 then
  1739.       result := ExtractFilePath(sl.Strings[0])
  1740.     else
  1741.       result := '';
  1742.   finally
  1743.     sl.free;
  1744.   end;
  1745. end;
  1746.  
  1747. function RecyclerGetPath(drive: char; IncludeInfofile: boolean): string; overload;
  1748. var
  1749.   sl: TStringList;
  1750. begin
  1751.   sl := TStringList.Create;
  1752.   try
  1753.     RecyclerGetInfofiles(drive, IncludeInfofile, sl);
  1754.     if sl.Count > 0 then
  1755.       result := ExtractFilePath(sl.Strings[0])
  1756.     else
  1757.       result := '';
  1758.   finally
  1759.     sl.free;
  1760.   end;
  1761. end;
  1762.  
  1763. function RecyclerGetPath(drive: char; UserSID: string): string; overload;
  1764. var
  1765.   sl: TStringList;
  1766. begin
  1767.   sl := TStringList.Create;
  1768.   try
  1769.     RecyclerGetInfofiles(drive, UserSID, sl);
  1770.     if sl.Count > 0 then
  1771.       result := ExtractFilePath(sl.Strings[0])
  1772.     else
  1773.       result := '';
  1774.   finally
  1775.     sl.free;
  1776.   end;
  1777. end;
  1778.  
  1779. function RecyclerGetPath(drive: char): string; overload;
  1780. var
  1781.   sl: TStringList;
  1782. begin
  1783.   sl := TStringList.Create;
  1784.   try
  1785.     RecyclerGetInfofiles(drive, sl);
  1786.     if sl.Count > 0 then
  1787.       result := ExtractFilePath(sl.Strings[0])
  1788.     else
  1789.       result := '';
  1790.   finally
  1791.     sl.free;
  1792.   end;
  1793. end;
  1794.  
  1795. ////////////////////////////////////////////////////////////////////////////////
  1796.  
  1797. procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string; result: TStringList); overload;
  1798. var
  1799.   dir: string;
  1800. begin
  1801.   (* if Win32Platform = VER_PLATFORM_WIN32_NT then
  1802.   begin *)
  1803.     if _isVista() then
  1804.     begin
  1805.       if _isFAT(drive) then
  1806.       begin
  1807.         dir := drive + DriveDelim + PathDelim + '$recycle.bin' + PathDelim;
  1808.         if IncludeInfofile and (fileid <> '') then
  1809.         begin
  1810.           if fileExists(dir + '$I'+fileid) then
  1811.             result.Add(dir + '$I'+fileid);
  1812.         end
  1813.         else
  1814.         begin
  1815.           if directoryExists(dir) then
  1816.             result.Add(dir);
  1817.         end;
  1818.       end
  1819.       else
  1820.       begin
  1821.         if UserSID <> '' then
  1822.         begin
  1823.           dir := drive + DriveDelim + PathDelim + '$recycle.bin'+PathDelim+UserSID+PathDelim;
  1824.           if IncludeInfofile and (fileid <> '') then
  1825.           begin
  1826.             if fileExists(dir + '$I'+fileid) then
  1827.               result.Add(dir + '$I'+fileid);
  1828.           end
  1829.           else
  1830.           begin
  1831.             if directoryExists(dir) then
  1832.               result.Add(dir);
  1833.           end;
  1834.         end
  1835.         else
  1836.         begin
  1837.           dir := drive + DriveDelim + PathDelim + '$recycle.bin'+PathDelim+_getMySID()+PathDelim;
  1838.           if IncludeInfofile and (fileid <> '') then
  1839.           begin
  1840.             if fileExists(dir + '$I'+fileid) then
  1841.               result.Add(dir + '$I'+fileid);
  1842.           end
  1843.           else
  1844.           begin
  1845.             if directoryExists(dir) then
  1846.               result.Add(dir);
  1847.           end;
  1848.         end;
  1849.       end;
  1850.     end
  1851.     else
  1852.     begin
  1853.       if _isFAT(drive) then
  1854.       begin
  1855.         dir := drive + DriveDelim + PathDelim + 'Recycled' + PathDelim;
  1856.         if IncludeInfofile then
  1857.         begin
  1858.           // Both "recycle bins" are possible if you have multiboot (but do overwrite themselfes if you empty them)
  1859.           if fileExists(dir + 'INFO2') then
  1860.             result.Add(dir + 'INFO2'); // Windows 95 with Internet Explorer 4 Extension or higher Windows versions
  1861.           if fileExists(dir + 'INFO') then
  1862.             result.Add(dir + 'INFO'); // Windows 95 native
  1863.         end
  1864.         else
  1865.         begin
  1866.           if directoryExists(dir) then
  1867.             result.Add(dir);
  1868.         end;
  1869.       end
  1870.       else
  1871.       begin
  1872.         if UserSID <> '' then
  1873.         begin
  1874.           dir := drive + DriveDelim + PathDelim + 'Recycler'+PathDelim+UserSID+PathDelim;
  1875.           if IncludeInfofile then
  1876.           begin
  1877.             if fileExists(dir + 'INFO2') then
  1878.               result.Add(dir + 'INFO2');
  1879.           end
  1880.           else
  1881.           begin
  1882.             if directoryExists(dir) then
  1883.               result.Add(dir);
  1884.           end;
  1885.         end
  1886.         else
  1887.         begin
  1888.           dir := drive + DriveDelim + PathDelim + 'Recycler'+PathDelim+_getMySID()+PathDelim;
  1889.           if IncludeInfofile then
  1890.           begin
  1891.             if fileExists(dir + 'INFO2') then
  1892.               result.Add(dir + 'INFO2');
  1893.           end
  1894.           else
  1895.           begin
  1896.             if directoryExists(dir) then
  1897.               result.Add(dir);
  1898.           end;
  1899.         end;
  1900.       end;
  1901.     end;
  1902.   (* end;
  1903.   else
  1904.   begin
  1905.     if isWin95() then
  1906.     begin
  1907.       result := drive + DriveDelim + PathDelim + 'Recycled' + PathDelim;
  1908.       if IncludeInfofile then result := result + 'INFO';
  1909.     end
  1910.     else
  1911.     begin
  1912.       result := drive + DriveDelim + PathDelim + 'Recycled' + PathDelim;
  1913.       if IncludeInfofile then result := result + 'INFO2';
  1914.     end;
  1915.   end; *)
  1916. end;
  1917.  
  1918. procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; result: TStringList); overload;
  1919. begin
  1920.   RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, '', result);
  1921. end;
  1922.  
  1923. procedure RecyclerGetInfofiles(drive: char; IncludeInfofile: boolean; result: TStringList); overload;
  1924. begin
  1925.   RecyclerGetInfofiles(drive, '', IncludeInfofile, '', result);
  1926. end;
  1927.  
  1928. procedure RecyclerGetInfofiles(drive: char; UserSID: string; result: TStringList); overload;
  1929. begin
  1930.   RecyclerGetInfofiles(drive, UserSID, false, '', result);
  1931. end;
  1932.  
  1933. procedure RecyclerGetInfofiles(drive: char; result: TStringList); overload;
  1934. begin
  1935.   RecyclerGetInfofiles(drive, '', false, '', result);
  1936. end;
  1937.  
  1938. ////////////////////////////////////////////////////////////////////////////////
  1939.  
  1940. function RecyclerCurrentFilenameAndPath(drive: char; UserSID: string; fileid: string): string; overload;
  1941. begin
  1942.   result := RecyclerGetPath(drive, UserSID, false, fileid) +
  1943.     RecyclerCurrentFilename(drive, UserSID, fileid);
  1944. end;
  1945.  
  1946. function RecyclerCurrentFilenameAndPath(drive: char; fileid: string): string; overload;
  1947. begin
  1948.   result := RecyclerCurrentFilenameAndPath(drive, '', fileid);
  1949. end;
  1950.  
  1951. function RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder: string; id: string): string; overload;
  1952. begin
  1953.   if RecyclerIsValid(InfofileOrRecycleFolder) then
  1954.   begin
  1955.     result := extractfilepath(InfofileOrRecycleFolder) +
  1956.       RecyclerCurrentFilename(InfofileOrRecycleFolder, id);
  1957.   end
  1958.   else
  1959.     result := '';
  1960. end;
  1961.  
  1962. ////////////////////////////////////////////////////////////////////////////////
  1963.  
  1964. function RecyclerRemoveItem(drive: char; UserSID: string; fileid: string): boolean; overload;
  1965. var
  1966.   tmp: string;
  1967. begin
  1968.   tmp := RecyclerCurrentFilenameAndPath(drive, UserSID, fileid);
  1969.   if fileexists(tmp) then
  1970.   begin
  1971.     deletefile(tmp);
  1972.     result := fileexists(tmp);
  1973.   end
  1974.   else
  1975.   begin
  1976.     directoryexists(tmp);
  1977.     result := directoryexists(tmp);
  1978.   end;
  1979. end;
  1980.  
  1981. function RecyclerRemoveItem(drive: char; fileid: string): boolean; overload;
  1982. begin
  1983.   result := RecyclerRemoveItem(drive, '', fileid);
  1984. end;
  1985.  
  1986. function RecyclerRemoveItem(InfofileOrRecycleFolder: string; id: string): boolean; overload;
  1987. var
  1988.   tmp: string;
  1989. begin
  1990.   tmp := RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder, id);
  1991.   if fileexists(tmp) then
  1992.   begin
  1993.     deletefile(tmp);
  1994.     result := fileexists(tmp);
  1995.   end
  1996.   else
  1997.   begin
  1998.     directoryexists(tmp);
  1999.     result := directoryexists(tmp);
  2000.   end;
  2001. end;
  2002.  
  2003. procedure RecyclerGetAllRecyclerDrives(result: TStringList);
  2004. var
  2005.   x: char;
  2006. begin
  2007.   for x := 'C' to 'Z' do
  2008.   begin
  2009.     if RecyclerIsValid(x) then
  2010.     begin
  2011.       result.Add(x);
  2012.     end;
  2013.   end;
  2014. end;
  2015.  
  2016. ////////////////////////////////////////////////////////////////////////////////
  2017.  
  2018. // http://www.dsdt.info/tipps/?id=176
  2019. function RecyclerEmptyRecycleBin(flags: cardinal): boolean; overload;
  2020. type
  2021.   TSHEmptyRecycleBin = function (Wnd: HWND;
  2022.                                  pszRootPath: PChar;
  2023.                                  dwFlags: DWORD):
  2024.                                  HRESULT; stdcall;
  2025. var
  2026.   PSHEmptyRecycleBin: TSHEmptyRecycleBin;
  2027.   LibHandle: THandle;
  2028. const
  2029.   C_SHEmptyRecycleBinA = 'SHEmptyRecycleBinA';
  2030. begin
  2031.   result := true;
  2032.   LibHandle := LoadLibrary(shell32) ;
  2033.   try
  2034.     if LibHandle <> 0 then
  2035.     begin
  2036.       @PSHEmptyRecycleBin:= GetProcAddress(LibHandle, C_SHEmptyRecycleBinA);
  2037.       if @PSHEmptyRecycleBin <> nil then
  2038.       begin
  2039.         PSHEmptyRecycleBin(hInstance, nil, flags);
  2040.       end
  2041.       else
  2042.         result := false;
  2043.     end
  2044.     else
  2045.       result := false;
  2046.   finally
  2047.     @PSHEmptyRecycleBin := nil;
  2048.     FreeLibrary(LibHandle);
  2049.   end;
  2050. end;
  2051.  
  2052. function RecyclerEmptyRecycleBin(sound, progress, confirmation: boolean): boolean; overload;
  2053. const
  2054.   SHERB_NOCONFIRMATION = $00000001;
  2055.   SHERB_NOPROGRESSUI   = $00000002;
  2056.   SHERB_NOSOUND        = $00000004;
  2057. var
  2058.   flags: cardinal;
  2059. begin
  2060.   flags := 0;
  2061.  
  2062.   if not progress then
  2063.     flags := flags or SHERB_NOPROGRESSUI;
  2064.   if not confirmation then
  2065.     flags := flags or SHERB_NOCONFIRMATION;
  2066.   if not sound then
  2067.     flags := flags or SHERB_NOSOUND;
  2068.  
  2069.   result := RecyclerEmptyRecycleBin(flags);
  2070. end;
  2071.  
  2072. ////////////////////////////////////////////////////////////////////////////////
  2073.  
  2074. // Template
  2075. // http://www.dsdt.info/tipps/?id=116
  2076. function RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean): boolean; overload;
  2077. var
  2078.   Operation: TSHFileOpStruct;
  2079. begin
  2080.   with Operation do
  2081.   begin
  2082.     Wnd := hInstance; // OK?
  2083.     wFunc := FO_DELETE;
  2084.     pFrom := PChar(FileOrFolder + #0);
  2085.     pTo := nil;
  2086.     fFlags := FOF_ALLOWUNDO;
  2087.     if not confirmation then fFlags := fFlags or FOF_NOCONFIRMATION;
  2088.   end;
  2089.   Result := SHFileOperation(Operation) = 0;
  2090. end;
  2091.  
  2092. function RecyclerAddFileOrFolder(FileOrFolder: string): boolean; overload;
  2093. begin
  2094.   result := RecyclerAddFileOrFolder(FileOrFolder, false);
  2095. end;
  2096.  
  2097. function RecyclerConfirmationDialogEnabled: boolean;
  2098. type
  2099.   TSHGetSettings = procedure (var lpss: SHELLSTATE; dwMask: DWORD) stdcall;
  2100. const
  2101.   C_SHGetSettings = 'SHGetSettings';
  2102. var
  2103.   lpss: SHELLSTATE;
  2104.   bNoConfirmRecycle: boolean;
  2105.  
  2106.   PSHGetSettings: TSHGetSettings;
  2107.   RBHandle: THandle;
  2108.  
  2109.   reg: TRegistry;
  2110.   rbuf: array[0..255] of byte;
  2111. begin
  2112.   PSHGetSettings := nil;
  2113.   result := false; // Avoid warning message
  2114.  
  2115.   RBHandle := LoadLibrary(shell32);
  2116.   if(RBHandle <> 0) then
  2117.   begin
  2118.     PSHGetSettings := GetProcAddress(RBHandle, C_SHGetSettings);
  2119.     if (@PSHGetSettings = nil) then
  2120.     begin
  2121.       FreeLibrary(RBHandle);
  2122.       RBHandle := 0;
  2123.     end;
  2124.   end;
  2125.  
  2126.   if (RBHandle <> 0) and (Assigned(PSHGetSettings)) then
  2127.   begin
  2128.     ZeroMemory(@lpss, SizeOf(lpss));
  2129.     PSHGetSettings(lpss, SSF_NOCONFIRMRECYCLE);
  2130.     // bNoConfirmRecycle := (lpss.Flags1 and 4) = 4; // fNoConfirmRecycle
  2131.     bNoConfirmRecycle := GetByteBit(lpss.Flags1, 2);
  2132.  
  2133.     result := not bNoConfirmRecycle;
  2134.   end
  2135.   else
  2136.   begin
  2137.     reg := TRegistry.Create;
  2138.     try
  2139.       // API function call failed. Probably because Windows is too old.
  2140.       // Try to read out from registry.
  2141.       // Warning: This method may be unsafe. In newer Windows versions
  2142.       // (Windows 2003 and higher) the 3rd bit of byte #5 seems to have
  2143.       // toggled in its behavior!! But for these newer Windows versions, our
  2144.       // API function should always be available.
  2145.  
  2146.       reg.RootKey := HKEY_CURRENT_USER;
  2147.       if (reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer')) then
  2148.       begin
  2149.         ZeroMemory(@rbuf, SizeOf(rbuf));
  2150.         reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf));
  2151.  
  2152.         // Lese 3tes Bit vom 5ten Byte
  2153.         // bNoConfirmRecycle := ((rbuf[4] and 4) = 4);
  2154.         bNoConfirmRecycle := GetByteBit(ord(rbuf[4]), 2);
  2155.         result := not bNoConfirmRecycle;
  2156.  
  2157.         reg.CloseKey;
  2158.       end
  2159.       else
  2160.       begin
  2161.         raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSettings])]);
  2162.       end;
  2163.     finally
  2164.       reg.Free;
  2165.     end;
  2166.   end;
  2167.  
  2168.   if (RBHandle <> 0) then FreeLibrary(RBHandle);
  2169. end;
  2170.  
  2171. procedure RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
  2172. type
  2173.   TSHGetSetSettings = procedure (var lpss: SHELLSTATE; dwMask: DWORD; bSet: BOOL) stdcall;
  2174. const
  2175.   C_SHGetSetSettings = 'SHGetSetSettings';
  2176. var
  2177.   lpss: SHELLSTATE;
  2178.  
  2179.   PSHGetSetSettings: TSHGetSetSettings;
  2180.   RBHandle: THandle;
  2181.  
  2182.   reg: TRegistry;
  2183.   rbuf: array[0..255] of byte;
  2184.  
  2185.   dwResult: DWORD;
  2186. begin
  2187.   PSHGetSetSettings := nil;
  2188.  
  2189.   RBHandle := LoadLibrary(shell32);
  2190.   if(RBHandle <> 0) then
  2191.   begin
  2192.     PSHGetSetSettings := GetProcAddress(RBHandle, C_SHGetSetSettings);
  2193.     if (@PSHGetSetSettings = nil) then
  2194.     begin
  2195.       FreeLibrary(RBHandle);
  2196.       RBHandle := 0;
  2197.     end;
  2198.   end;
  2199.  
  2200.   if (RBHandle <> 0) and (Assigned(PSHGetSetSettings)) then
  2201.   begin
  2202.     ZeroMemory(@lpss, SizeOf(lpss));
  2203.     PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, false); // Get
  2204.     lpss.Flags1 := SetByteBit(lpss.Flags1, 2, NewSetting);
  2205.     PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, true); // Set
  2206.  
  2207.     // TODO: Do we need a WM_SETTINGCHANGE message to send?
  2208.     SendMessageTimeout (
  2209.       HWND_BROADCAST, WM_SETTINGCHANGE,
  2210.       0, lParam (pChar ('Environment')),
  2211.       SMTO_ABORTIFHUNG, 5000, dwResult
  2212.     );
  2213.   end
  2214.   else
  2215.   begin
  2216.     reg := TRegistry.Create;
  2217.     try
  2218.       // API function call failed. Probably because Windows is too old.
  2219.       // Try to read out from registry.
  2220.       // Warning: This method may be unsafe. In newer Windows versions
  2221.       // (Windows 2003 and higher) the 3rd bit of byte #5 seems to have
  2222.       // toggled in its behavior!! But for these newer Windows versions, our
  2223.       // API function should always be available.
  2224.  
  2225.       reg.RootKey := HKEY_CURRENT_USER;
  2226.       if (reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer', false)) then
  2227.       begin
  2228.         ZeroMemory(@rbuf, SizeOf(rbuf));
  2229.         reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Get
  2230.         rbuf[4] := SetByteBit(rbuf[4], 2, NewSetting);
  2231.         reg.WriteBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Set
  2232.  
  2233.         // TODO: Check if that's OK...
  2234.         SendMessageTimeout (
  2235.           HWND_BROADCAST, WM_SETTINGCHANGE,
  2236.           0, lParam (pChar ('Environment')),
  2237.           SMTO_ABORTIFHUNG, 5000, dwResult
  2238.         );
  2239.  
  2240.         reg.CloseKey;
  2241.       end
  2242.       else
  2243.       begin
  2244.         raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSetSettings])]);
  2245.       end;
  2246.     finally
  2247.       reg.Free;
  2248.     end;
  2249.   end;
  2250.  
  2251.   if (RBHandle <> 0) then FreeLibrary(RBHandle);
  2252. end;
  2253.  
  2254. function RecyclerGetCurrentIconString: string;
  2255. begin
  2256.   if RecyclerIsEmpty then
  2257.     result := RecyclerGetEmptyIconString
  2258.   else
  2259.     result := RecyclerGetFullIconString;
  2260. end;
  2261.  
  2262. function RecyclerGetDefaultIconString: string;
  2263. var
  2264.   reg: TRegistry;
  2265. begin
  2266.   // Please note: The "default" icon is not always the icon of the
  2267.   // current recycle bin in its current state (full, empty)
  2268.   // At Windows 95b, the registry value actually did change every time the
  2269.   // recycle bin state did change, but at Windows 2000 I could not see any
  2270.   // update, even after reboot. So, the registry value is possible fixed as
  2271.   // default = empty on newer OS versions.
  2272.  
  2273.   reg := TRegistry.Create;
  2274.   try
  2275.     reg.RootKey := HKEY_CLASSES_ROOT;
  2276.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
  2277.     begin
  2278.       result := reg.ReadString('');
  2279.       reg.CloseKey;
  2280.     end;
  2281.   finally
  2282.     reg.Free;
  2283.   end;
  2284. end;
  2285.  
  2286. function RecyclerGetEmptyIconString: string;
  2287. var
  2288.   reg: TRegistry;
  2289. begin
  2290.   reg := TRegistry.Create;
  2291.   try
  2292.     reg.RootKey := HKEY_CLASSES_ROOT;
  2293.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
  2294.     begin
  2295.       result := reg.ReadString('Empty');
  2296.       reg.CloseKey;
  2297.     end;
  2298.   finally
  2299.     reg.Free;
  2300.   end;
  2301. end;
  2302.  
  2303. function RecyclerGetFullIconString: string;
  2304. var
  2305.   reg: TRegistry;
  2306. begin
  2307.   reg := TRegistry.Create;
  2308.   try
  2309.     reg.RootKey := HKEY_CLASSES_ROOT;
  2310.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
  2311.     begin
  2312.       result := reg.ReadString('Full');
  2313.       reg.CloseKey;
  2314.     end;
  2315.   finally
  2316.     reg.Free;
  2317.   end;
  2318. end;
  2319.  
  2320. function RecyclerGetName: string;
  2321. var
  2322.   reg: TRegistry;
  2323. begin
  2324.   // Windows 95b:
  2325.   // Change of CLSID\{645FF040-5081-101B-9F08-00AA002F954E} will change the desktop name of the recycle bin.
  2326.  
  2327.   // Windows 2000: If LocalizedString is available, the 3rd argument will be parsed
  2328.   // (if the third argument will removed, it will be read out from the DLL resource string automatically)
  2329.  
  2330.   reg := TRegistry.Create;
  2331.   try
  2332.     reg.RootKey := HKEY_CLASSES_ROOT;
  2333.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
  2334.     begin
  2335.       if reg.ValueExists('LocalizedString') then
  2336.       begin
  2337.         result := reg.ReadString('LocalizedString');
  2338.         result := _DecodeReferenceString(result);
  2339.       end
  2340.       else
  2341.       begin
  2342.         result := reg.ReadString('');
  2343.       end;
  2344.  
  2345.       reg.CloseKey;
  2346.     end;
  2347.   finally
  2348.     reg.Free;
  2349.   end;
  2350. end;
  2351.  
  2352. function RecyclerGetInfoTip: string;
  2353. var
  2354.   reg: TRegistry;
  2355. begin
  2356.   // Not available in some older versions of Windows
  2357.  
  2358.   reg := TRegistry.Create;
  2359.   try
  2360.     reg.RootKey := HKEY_CLASSES_ROOT;
  2361.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
  2362.     begin
  2363.       result := reg.ReadString('InfoTip');
  2364.       result := _DecodeReferenceString(result);
  2365.  
  2366.       reg.CloseKey;
  2367.     end;
  2368.   finally
  2369.     reg.Free;
  2370.   end;
  2371. end;
  2372.  
  2373. function RecyclerGetIntroText: string;
  2374. var
  2375.   reg: TRegistry;
  2376. begin
  2377.   // Not available in some older versions of Windows
  2378.  
  2379.   reg := TRegistry.Create;
  2380.   try
  2381.     reg.RootKey := HKEY_CLASSES_ROOT;
  2382.     if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
  2383.     begin
  2384.       result := reg.ReadString('IntroText');
  2385.       result := _DecodeReferenceString(result);
  2386.  
  2387.       reg.CloseKey;
  2388.     end;
  2389.   finally
  2390.     reg.Free;
  2391.   end;
  2392. end;
  2393.  
  2394. function RecyclerEmptyEventGetName: string;
  2395. var
  2396.   reg: TRegistry;
  2397. begin
  2398.   reg := TRegistry.Create;
  2399.   try
  2400.     reg.RootKey := HKEY_CURRENT_USER;
  2401.     if reg.OpenKeyReadOnly('AppEvents\EventLabels\EmptyRecycleBin') then
  2402.     begin
  2403.       result := reg.ReadString('');
  2404.       reg.CloseKey;
  2405.     end;
  2406.   finally
  2407.     reg.Free;
  2408.   end;
  2409. end;
  2410.  
  2411. function RecyclerEmptyEventGetCurrentSound: string;
  2412. begin
  2413.   result := RecyclerEmptyEventGetSound('.Current');
  2414. end;
  2415.  
  2416. function RecyclerEmptyEventGetDefaultSound: string;
  2417. begin
  2418.   result := RecyclerEmptyEventGetSound('.Default');
  2419. end;
  2420.  
  2421. procedure RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
  2422. var
  2423.   reg: TRegistry;
  2424. begin
  2425.   reg := TRegistry.Create;
  2426.   try
  2427.     reg.RootKey := HKEY_CURRENT_USER;
  2428.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2429.     begin
  2430.       reg.GetKeyNames(AStringList);
  2431.       reg.CloseKey;
  2432.     end;
  2433.   finally
  2434.     reg.Free;
  2435.   end;
  2436. end;
  2437.  
  2438. function RecyclerEmptyEventGetSound(ACategory: string): string;
  2439. var
  2440.   reg: TRegistry;
  2441. resourcestring
  2442.   LNG_SND_EVENT_CAT_ERROR = 'The category "%s" is not available for the notification event "%s".';
  2443. begin
  2444.   // Outputs an filename or empty string for no sound defined.
  2445.  
  2446.   reg := TRegistry.Create;
  2447.   try
  2448.     reg.RootKey := HKEY_CURRENT_USER;
  2449.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2450.     begin
  2451.       if reg.OpenKeyReadOnly(ACategory) then
  2452.       begin
  2453.         result := reg.ReadString('');
  2454.         reg.CloseKey;
  2455.       end
  2456.       else
  2457.         raise EEventCategoryNotDefined.CreateFmt(LNG_SND_EVENT_CAT_ERROR, [ACategory, 'EmptyRecycleBin']);
  2458.       reg.CloseKey;
  2459.     end;
  2460.   finally
  2461.     reg.Free;
  2462.   end;
  2463. end;
  2464.  
  2465. function RecyclerGlobalGetPercentUsage: integer;
  2466. var
  2467.   reg: TRegistry;
  2468.   dump: string;
  2469. const
  2470.   RES_DEFAULT = 10;
  2471. begin
  2472.   result := -1;
  2473.  
  2474.   reg := TRegistry.Create;
  2475.   try
  2476.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2477.  
  2478.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  2479.     begin
  2480.       if reg.ValueExists('Percent') then
  2481.       begin
  2482.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  2483.  
  2484.         result := reg.ReadInteger('Percent');
  2485.       end
  2486.       else if reg.ValueExists('PurgeInfo') then
  2487.       begin
  2488.         // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
  2489.  
  2490.         dump := _registryReadDump(reg, 'PurgeInfo');
  2491.         result := Ord(dump[63]);
  2492.       end
  2493.       else
  2494.       begin
  2495.         // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
  2496.  
  2497.         result := RES_DEFAULT; // Standardeinstellung bei Windows
  2498.       end;
  2499.  
  2500.       reg.CloseKey;
  2501.     end;
  2502.   finally
  2503.     reg.Free;
  2504.   end;
  2505. end;
  2506.  
  2507. function RecyclerSpecificGetPercentUsage(Drive: Char): integer;
  2508. var
  2509.   reg: TRegistry;
  2510.   dump: string;
  2511. const
  2512.   RES_DEFAULT = 10;
  2513. begin
  2514.   result := -1;
  2515.  
  2516.   reg := TRegistry.Create;
  2517.   try
  2518.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2519.  
  2520.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  2521.     begin
  2522.       if reg.OpenKeyReadOnly(Drive) then
  2523.       begin
  2524.         if reg.ValueExists('Percent') then
  2525.         begin
  2526.           // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  2527.  
  2528.           result := reg.ReadInteger('Percent');
  2529.         end
  2530.         else
  2531.         begin
  2532.           result := RES_DEFAULT;
  2533.         end;
  2534.         reg.CloseKey;
  2535.       end
  2536.       else
  2537.       begin
  2538.         if reg.ValueExists('PurgeInfo') then
  2539.         begin
  2540.           // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
  2541.  
  2542.           dump := _registryReadDump(reg, 'PurgeInfo');
  2543.  
  2544.           // NOT tested, only theoretical! My idea about the possible structure is:
  2545.           // 0x08 = Drive A
  2546.           // 0x0a = Drive B
  2547.           // 0x0c = Drive C (validated)
  2548.           // 0x0e = Drive D
  2549.           // ...
  2550.  
  2551.           result := Ord(dump[9+_DriveNum(Drive)*2]);
  2552.         end
  2553.         else
  2554.         begin
  2555.           // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
  2556.  
  2557.           result := RES_DEFAULT; // Standardeinstellung bei Windows
  2558.         end;
  2559.       end;
  2560.  
  2561.       reg.CloseKey;
  2562.     end;
  2563.   finally
  2564.     reg.Free;
  2565.   end;
  2566. end;
  2567.  
  2568. function RecyclerGetPercentUsageAutoDeterminate(Drive: Char): integer;
  2569. begin
  2570.   if RecyclerHasGlobalSettings then
  2571.     result := RecyclerGlobalGetPercentUsage
  2572.   else
  2573.     result := RecyclerSpecificGetPercentUsage(Drive);
  2574. end;
  2575.  
  2576. function RecyclerGlobalIsNukeOnDelete: boolean;
  2577. var
  2578.   reg: TRegistry;
  2579.   dump: string;
  2580. const
  2581.   RES_DEFAULT = false;
  2582. begin
  2583.   result := false;
  2584.  
  2585.   reg := TRegistry.Create;
  2586.   try
  2587.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2588.  
  2589.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  2590.     begin
  2591.       if reg.ValueExists('NukeOnDelete') then
  2592.       begin
  2593.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  2594.  
  2595.         result := reg.ReadBool('NukeOnDelete');
  2596.       end
  2597.       else if reg.ValueExists('PurgeInfo') then
  2598.       begin
  2599.         // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
  2600.  
  2601.         // See comment at RecyclerSpecificIsNukeOnDelete()
  2602.  
  2603.         dump := _registryReadDump(reg, 'PurgeInfo');
  2604.         result := _GetBit(dump[68], 3);
  2605.       end
  2606.       else
  2607.       begin
  2608.         // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
  2609.  
  2610.         result := RES_DEFAULT; // Standardeinstellung bei Windows
  2611.       end;
  2612.  
  2613.       reg.CloseKey;
  2614.     end;
  2615.   finally
  2616.     reg.Free;
  2617.   end;
  2618. end;
  2619.  
  2620. function RecyclerSpecificIsNukeOnDelete(Drive: Char): boolean;
  2621. var
  2622.   reg: TRegistry;
  2623.   dump: string;
  2624.   d: Byte;
  2625. const
  2626.   RES_DEFAULT = false;
  2627. begin
  2628.   result := false;
  2629.  
  2630.   reg := TRegistry.Create;
  2631.   try
  2632.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2633.  
  2634.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  2635.     begin
  2636.       if reg.OpenKeyReadOnly(Drive) then
  2637.       begin
  2638.         if reg.ValueExists('NukeOnDelete') then
  2639.         begin
  2640.           // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  2641.  
  2642.           result := reg.ReadBool('NukeOnDelete');
  2643.         end;
  2644.         reg.CloseKey;
  2645.       end
  2646.       else
  2647.       begin
  2648.         if reg.ValueExists('PurgeInfo') then
  2649.         begin
  2650.           // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
  2651.  
  2652.           dump := _registryReadDump(reg, 'PurgeInfo');
  2653.  
  2654.           // NOT tested, only theoretical! My idea about the possible structure is:
  2655.           //
  2656.           // Byte      0x40       0x41       0x42       0x43
  2657.           // Bit       76543210   76543210   76543210   76543210
  2658.           //           --------   --------   --------   --------
  2659.           // Meaning   hgfedcba   ponmlkji   xwvutsrq   ????G?zy
  2660.           //
  2661.           // a..z = Drives
  2662.           // G    = global settings
  2663.           //
  2664.           // Already validated:
  2665.           // 0x64 = 04 (00000100)
  2666.           // 0x67 = 08 (00001000)
  2667.  
  2668.           d := _DriveNum(Drive);
  2669.           result := _GetBit(dump[65+(d div 7)], d mod 7);
  2670.         end
  2671.         else
  2672.         begin
  2673.           // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
  2674.  
  2675.           result := RES_DEFAULT; // Standardeinstellung bei Windows
  2676.         end;
  2677.       end;
  2678.  
  2679.       reg.CloseKey;
  2680.     end;
  2681.   finally
  2682.     reg.Free;
  2683.   end;
  2684. end;
  2685.  
  2686. function RecyclerIsNukeOnDeleteAutoDeterminate(Drive: Char): boolean;
  2687. begin
  2688.   if RecyclerHasGlobalSettings then
  2689.     result := RecyclerGlobalIsNukeOnDelete
  2690.   else
  2691.     result := RecyclerSpecificIsNukeOnDelete(Drive);
  2692. end;
  2693.  
  2694. function RecyclerHasGlobalSettings: boolean;
  2695. var
  2696.   reg: TRegistry;
  2697.   dump: string;
  2698. begin
  2699.   result := false;
  2700.  
  2701.   reg := TRegistry.Create;
  2702.   try
  2703.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2704.  
  2705.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  2706.     begin
  2707.       if reg.ValueExists('UseGlobalSettings') then
  2708.       begin
  2709.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  2710.  
  2711.         result := reg.ReadBool('UseGlobalSettings');
  2712.       end
  2713.       else if reg.ValueExists('PurgeInfo') then
  2714.       begin
  2715.         // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
  2716.         // TODO: Gibt es ein offizielles Dokument oder ein API, indem PurgeInfo
  2717.         // offiziell entschlüsselbar ist?
  2718.  
  2719.         dump := _registryReadDump(reg, 'PurgeInfo');
  2720.         if dump[5] = #$01 then
  2721.           result := true
  2722.         else if dump[5] = #$00 then
  2723.           result := false
  2724.         else
  2725.           raise EUnknownState.Create(Format(LNG_UNEXPECTED_STATE, ['PurgeInfo']));
  2726.       end
  2727.       else
  2728.       begin
  2729.         // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
  2730.  
  2731.         result := true; // Standardeinstellung bei Windows
  2732.       end;
  2733.  
  2734.       reg.CloseKey;
  2735.     end;
  2736.   finally
  2737.     reg.Free;
  2738.   end;
  2739. end;
  2740.  
  2741. function RecyclerIsEmpty: boolean;
  2742. var
  2743.   Drive: Char;
  2744. begin
  2745.   result := true;
  2746.   for Drive := 'A' to 'Z' do
  2747.   begin
  2748.     if not RecyclerIsEmpty(Drive) then
  2749.     begin
  2750.       result := false;
  2751.       exit;
  2752.     end;
  2753.   end;
  2754. end;
  2755.  
  2756. function RecyclerIsEmpty(Drive: Char): boolean;
  2757. begin
  2758.   result := RecyclerGetAPIInfo(Drive).i64NumItems = 0;
  2759. end;
  2760.  
  2761. function RecyclerGetNumItems: int64;
  2762. var
  2763.   Drive: Char;
  2764. begin
  2765.   result := 0;
  2766.   for Drive := 'A' to 'Z' do
  2767.   begin
  2768.     result := result + RecyclerGetNumItems(Drive);
  2769.   end;
  2770. end;
  2771.  
  2772. function RecyclerGetNumItems(Drive: Char): int64;
  2773. begin
  2774.   result := RecyclerGetAPIInfo(Drive).i64NumItems;
  2775. end;
  2776.  
  2777. function RecyclerGetSize: int64;
  2778. var
  2779.   Drive: Char;
  2780. begin
  2781.   result := 0;
  2782.   for Drive := 'A' to 'Z' do
  2783.   begin
  2784.     result := result + RecyclerGetSize(Drive);
  2785.   end;
  2786. end;
  2787.  
  2788. function RecyclerGetSize(Drive: Char): int64;
  2789. begin
  2790.   result := RecyclerGetAPIInfo(Drive).i64Size;
  2791. end;
  2792.  
  2793. function RecyclerGetAPIInfo(Drive: Char): TSHQueryRBInfo;
  2794. begin
  2795.   result := RecyclerGetAPIInfo(Drive + ':\');
  2796. end;
  2797.  
  2798. const
  2799.   C_SHQueryRecycleBin = 'SHQueryRecycleBinA';
  2800.  
  2801. type
  2802.   TSHQueryRecycleBin = function(pszRootPath: LPCTSTR;
  2803.     var pSHQueryRBInfo: TSHQueryRBInfo): HRESULT; stdcall;
  2804.  
  2805. function RecyclerGetAPIInfo(Path: String): TSHQueryRBInfo;
  2806. var
  2807.   PSHQueryRecycleBin: TSHQueryRecycleBin;
  2808.   RBHandle: THandle;
  2809.   res: HRESULT;
  2810. begin
  2811.   PSHQueryRecycleBin := nil;
  2812.  
  2813.   // Ref: http://www.delphipraxis.net/post1291.html
  2814.  
  2815.   RBHandle := LoadLibrary(shell32);
  2816.   if(RBHandle <> 0) then
  2817.   begin
  2818.     PSHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  2819.     if(@PSHQueryRecycleBin = nil) then
  2820.     begin
  2821.       FreeLibrary(RBHandle);
  2822.       RBHandle := 0;
  2823.     end;
  2824.   end;
  2825.  
  2826.   fillchar(result, SizeOf(TSHQueryRBInfo),0);
  2827.   result.cbSize := SizeOf(TSHQueryRBInfo);
  2828.  
  2829.   if (RBHandle <> 0) and (Assigned(PSHQueryRecycleBin)) then
  2830.   begin
  2831.     res := PSHQueryRecycleBin(PChar(Path), result);
  2832.     // if Succeeded(res) then
  2833.     if res = S_OK then
  2834.     begin
  2835.       // Alles OK, unser result hat nun die gewünschten Daten.
  2836.     end
  2837.     else
  2838.       raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_ERROR_CODE, [C_SHQueryRecycleBin, '0x'+IntToHex(res, 2*SizeOf(HRESULT))])]);
  2839.   end
  2840.   else
  2841.     raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHQueryRecycleBin])]);
  2842.  
  2843.   if (RBHandle <> 0) then FreeLibrary(RBHandle);
  2844. end;
  2845.  
  2846. function RecyclerGetCLSID: string;
  2847. begin
  2848.   result := RECYCLER_CLSID;
  2849. end;
  2850.  
  2851. // Windows 95 without Internet Explorer 4 has not SHQueryRecycleBinA.
  2852. function RecyclerQueryFunctionAvailable: boolean;
  2853. var
  2854.   RBHandle: THandle;
  2855.   SHQueryRecycleBin: TSHQueryRecycleBin;
  2856. begin
  2857.   RBHandle := LoadLibrary(shell32);
  2858.   if(RBHandle <> 0) then
  2859.   begin
  2860.     SHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  2861.     if(@SHQueryRecycleBin = nil) then
  2862.     begin
  2863.       FreeLibrary(RBHandle);
  2864.       RBHandle := 0;
  2865.     end;
  2866.   end;
  2867.  
  2868.   result := RBHandle <> 0;
  2869. end;
  2870.  
  2871. function RecyclerLibraryVersion: string;
  2872. begin
  2873.   result := 'ViaThinkSoft Recycle Bin Unit [16 JUN 2010]';
  2874. end;
  2875.  
  2876. end.
  2877.