Subversion Repositories recyclebinunit

Rev

Rev 89 | Rev 93 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit RecBinUnit2 platform;
  2.  
  3. ////////////////////////////////////////////////////////////////////////////////////
  4. // RECYCLE-BIN-UNIT V2 BY DANIEL MARSCHALL, VIATHINKSOFT                          //
  5. // E-MAIL: info@daniel-marschall.de                                               //
  6. // Web:    www.daniel-marschall.de & www.viathinksoft.de                          //
  7. ////////////////////////////////////////////////////////////////////////////////////
  8. // Revision: 30 JUN 2022                                                          //
  9. // This unit is freeware, but please link to my website if you are using it!      //
  10. ////////////////////////////////////////////////////////////////////////////////////
  11. // Successfully tested with:                                                      //
  12. // Windows 95b (without IE4 Shell Extensions)                                     //
  13. // Windows 95b (with IE4 Shell Extensions)                                        //
  14. // Windows 98 SE                                                                  //
  15. // Windows NT4 SP6                                                                //
  16. // Windows XP SP3                                                                 //
  17. // Windows 2000 SP4                                                               //
  18. // Windows 2003 Server EE SP1                                                     //
  19. // Windows Vista                                                                  //
  20. // Windows 7                                                                      //
  21. // Windows 10 (version 1 and version 2 format)                                    //
  22. // Windows 11                                                                     //
  23. ////////////////////////////////////////////////////////////////////////////////////
  24.  
  25. // Delphi 7 Compatibility:  (TODO: compiler switches)
  26. // - Remove "static"
  27. // - Remove "strict"
  28. // - Remove "$REGION"
  29.  
  30. // TODO: ReadBuffer überall try-except
  31. // TODO: Always check EOF before reading anything?
  32. // TODO: Don't crash when timestamp is invalid. Do something else instead.
  33. // TODO: Is it possible to identify a Vista-file that is not named $Ixxxxxx.ext?
  34. // TODO: RecyclerGetInfofiles() check additionally for removable device?
  35. //       RecyclerIsValid() is false.
  36. // TODO: Make it possible to empty the recycle bin of one specific drive!
  37. // TODO: Unknown! Do you know the answer?
  38. //       - How does Windows 9x/NT manage the daylight saving time (if it does)?
  39. //       - How does Windows Vista+ react to a RECYCLER\ folder on a NTFS device?
  40. //       - How does Windows Vista+ react to a RECYCLED\ folder on a FAT device? ==> Win7: is ignored!
  41. //       - How does Windows XP react to RECYCLED\ folder on a FAT device?
  42. // TODO: Translate all comments from German to English
  43. // TODO: Do we need this (maybe not all drives have A: till Z:?) http://stackoverflow.com/questions/17110543/how-to-retrieve-the-disk-signature-of-all-the-disks-in-windows-using-delphi-7
  44. // TODO: Add a lot of setters for system config stuff
  45.  
  46. // If enabled, the deletion timestamps will not be converted by the WinAPI.
  47. {.$DEFINE FILETIME_DELPHI_CODE}
  48.  
  49. // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered, even if gpedit.msc shows "Not configured"!
  50. {$DEFINE GroupPolicyAcceptHKLMTrick}
  51.  
  52. interface
  53.  
  54. uses
  55.   Windows, SysUtils, Classes, ContNrs, ShellAPI, Registry, Messages, Math;
  56.  
  57. const
  58.   RECBINUNIT_VERSION = '2022-06-30';
  59.  
  60.   RECYCLER_CLSID: TGUID = '{645FF040-5081-101B-9F08-00AA002F954E}';
  61.   NULL_GUID:      TGUID = '{00000000-0000-0000-0000-000000000000}';
  62.  
  63. type
  64.   EAPICallError = class(Exception);
  65.   EEventCategoryNotDefined = class(Exception);
  66.   EInvalidDrive = class(Exception);
  67.  
  68.   PSHQueryRBInfo = ^TSHQueryRBInfo;
  69.   TSHQueryRBInfo = packed record
  70.     cbSize      : DWORD;
  71.     i64Size     : int64;
  72.     i64NumItems : int64;
  73.   end;
  74.  
  75.   TRbRecycleBinItem = class(TObject)
  76.   strict private
  77.     function GetSource: string;
  78.   strict protected
  79.     FSourceAnsi: AnsiString;
  80.     FSourceUnicode: WideString;
  81.     FID: string;
  82.     FSourceDrive: Char;
  83.     FDeletionTime: TDateTime;
  84.     FOriginalSize: int64;
  85.     FIndexFile: string;
  86.     FRemovedEntry: boolean;
  87.     procedure ReadFromStream(stream: TStream); virtual; abstract;
  88.     function GetPhysicalFile: string; virtual; abstract; // protected, because it will be read by "property"
  89.   public
  90.     property PhysicalFile: string read GetPhysicalFile;
  91.     property SourceAnsi: AnsiString read FSourceAnsi;
  92.     property SourceUnicode: WideString read FSourceUnicode;
  93.     property Source: string read GetSource; // will bei either ANSI or Unicode, depending on the Delphi version
  94.     property ID: string read FID;
  95.     property SourceDrive: Char read FSourceDrive;
  96.     property DeletionTime: TDateTime read FDeletionTime;
  97.     property OriginalSize: int64 read FOriginalSize;
  98.     property IndexFile: string read FIndexFile;
  99.     property RemovedEntry: boolean read FRemovedEntry; // the file is NOT in the recycle bin anymore!
  100.  
  101.     // Attention: There are no official API calls. The delete and recover
  102.     // functions might fail and/or damage the shell cache. Handle with care!
  103.     function DeleteFile: boolean; virtual; abstract;
  104.     function RecoverFile: boolean; virtual; abstract;
  105.     function OpenFile: boolean; virtual; abstract;
  106.   end;
  107.  
  108.   TRbInfoAItem = class(TRbRecycleBinItem)
  109.   strict protected
  110.     procedure ReadFromStream(stream: TStream); override;
  111.     function GetPhysicalFile: string; override;
  112.   public
  113.     constructor Create(fs: TStream; AIndexFile: string);
  114.     function DeleteFile: boolean; override;
  115.     // TODO: function RecoverFile: boolean; override;
  116.     // TODO: function OpenFile: boolean; override;
  117.   end;
  118.  
  119.   TRbInfoWItem = class(TRbRecycleBinItem)
  120.   strict protected
  121.     procedure ReadFromStream(stream: TStream); override;
  122.     function GetPhysicalFile: string; override;
  123.   public
  124.     constructor Create(fs: TStream; AIndexFile: string);
  125.     function DeleteFile: boolean; override;
  126.     // TODO: function RecoverFile: boolean; override;
  127.     // TODO: function OpenFile: boolean; override;
  128.   end;
  129.  
  130.   TRbVistaItem = class(TRbRecycleBinItem)
  131.   strict protected
  132.     procedure ReadFromStream(stream: TStream); override;
  133.     function GetPhysicalFile: string; override;
  134.   public
  135.     constructor Create(fs: TStream; AIndexFile, AID: string);
  136.     function DeleteFile: boolean; override;
  137.     // TODO: function RecoverFile: boolean; override;
  138.     // TODO: function OpenFile: boolean; override;
  139.   end;
  140.  
  141.   TRbRecycleBin = class(TObject)
  142.   strict private
  143.     FFileOrDirectory: string;
  144.     FSID: string;
  145.     FTolerantReading: boolean;
  146.   public
  147.     constructor Create(AFileOrDirectory: string; ASID: string='');
  148.  
  149.     function GetItem(id: string): TRbRecycleBinItem;
  150.     procedure ListItems(list: TObjectList{TRbRecycleBinItem});
  151.     function CheckIndexes(slErrors: TStrings): boolean;
  152.  
  153.     property FileOrDirectory: string read FFileOrDirectory;
  154.     property SID: string read FSID;
  155.  
  156.     // Allows an index file to be read, even if an incompatible multiboot combination
  157.     // corrupted it. Default: true.
  158.     property TolerantReading: boolean read FTolerantReading write FTolerantReading;
  159.   end;
  160.  
  161.   // TODO: Wie sieht es aus mit Laufwerken, die nur als Mount-Point eingebunden sind?
  162.   TRbDrive = class(TObject)
  163.   strict private
  164.     FDriveLetter: AnsiChar;
  165.  
  166.     function OldCapacityPercent(var res: integer): boolean; // in % (0-100)
  167.     function NewCapacityAbsolute(var res: integer): boolean; // in MB
  168.  
  169.     function DiskSize: integer; // in MB
  170.     function DriveNumber: integer;
  171.   strict protected
  172.     function IsFAT: boolean;
  173.     procedure CheckDriveExisting;
  174.  
  175.     // will return NULL_GUID in case of an error or if it is not supported
  176.     function GetVolumeGUID: TGUID;
  177.     function GetVolumeGUIDAvailable: boolean;
  178.  
  179.     // TODO: get drive serial
  180.   public
  181.     constructor Create(ADriveLetter: AnsiChar);
  182.  
  183.     // Wenn UserSID='', dann werden alle Recycler gefunden
  184.     procedure ListRecycleBins(list: TObjectList{TRbRecycleBin}; UserSID: string='');
  185.  
  186.     property DriveLetter: AnsiChar read FDriveLetter;
  187.     property VolumeGUID: TGUID read GetVolumeGUID;
  188.     property VolumeGUIDAvailable: boolean read GetVolumeGUIDAvailable;
  189.     function GetAPIInfo: TSHQueryRBInfo;
  190.     function GetSize: int64;
  191.     function GetNumItems: int64;
  192.     function IsEmpty: boolean;
  193.  
  194.     function GetMaxPercentUsage: Extended; // 0..1
  195.     function GetMaxAbsoluteUsage: integer; // in MB
  196.     function GetNukeOnDelete: boolean;
  197.   end;
  198.  
  199.   GPOLICYBOOL = (gpUndefined, gpEnabled, gpDisabled);
  200.  
  201.   TRecycleBinManager = class(TObject)
  202.   public
  203.     class procedure ListDrives(list: TObjectList{TRbDrive}); static;
  204.     class function RecycleBinPossible(Drive: AnsiChar): boolean; static;
  205.  
  206.     class function OwnRecyclersSize: int64; static;
  207.     class function OwnRecyclersNumItems: int64; static;
  208.     class function OwnRecyclersEmpty: boolean; static;
  209.  
  210.     class function EmptyOwnRecyclers(flags: cardinal): boolean; overload; static;
  211.     class function EmptyOwnRecyclers(sound, progress, confirmation: boolean): boolean; overload; static;
  212.  
  213.     class function RecyclerGetCurrentIconString: string; static;
  214.     class function RecyclerGetDefaultIconString: string; static;
  215.     class function RecyclerGetEmptyIconString: string; static;
  216.     class function RecyclerGetFullIconString: string; static;
  217.  
  218.     class function GetGlobalMaxPercentUsage: integer; static; // TODO: In Win Vista: absolute and not relative sizes
  219.     class function GetGlobalNukeOnDelete: boolean; static;
  220.     class function UsesGlobalSettings: boolean; static;
  221.  
  222.     class function RecyclerGetName: string; static;
  223.     class function RecyclerGetInfoTip: string; static;
  224.     class function RecyclerGetIntroText: string; static;
  225.  
  226.     class function RecyclerEmptyEventGetCurrentSound: string; static;
  227.     class function RecyclerEmptyEventGetDefaultSound: string; static;
  228.     class function RecyclerEmptyEventGetName: string; static;
  229.     class function RecyclerEmptyEventGetSound(ACategory: string): string; static;
  230.     class procedure RecyclerEmptyEventGetSoundCategories(AStringList: TStringList); static;
  231.  
  232.     // TODO: In future also detect for other users
  233.     // TODO: Also make a setter (incl. Message to Windows Explorer?)
  234.     class function RecyclerGroupPolicyConfirmFileDelete: GPOLICYBOOL; static;
  235.     class function RecyclerGroupPolicyNoRecycleFiles: GPOLICYBOOL; static;
  236.     class function RecyclerGroupPolicyRecycleBinSize: integer; static;
  237.  
  238.     class function RecyclerConfirmationDialogEnabled: boolean; static;
  239.     class procedure RecyclerConfirmationDialogSetEnabled(NewSetting: boolean); static;
  240.     class function RecyclerShellStateConfirmationDialogEnabled: boolean; static;
  241.  
  242.     // Diese Funktion ist false, wenn sie z.B. unter Windows 95 ohne Internet Explorer
  243.     // 4.0 Shell Extension ausgeführt wird. Wenn abwärtskompatibler Code geschrieben
  244.     // werden soll, sollte RecyclerQueryFunctionAvailable() verwendet werden, da
  245.     // unter Windows 95 folgende Funktionalitäten NICHT vorhanden sind:
  246.     // - RecyclerIsEmpty
  247.     // - RecyclerGetNumItems
  248.     // - RecyclerGetSize
  249.     // - RecyclerGetAPIInfo
  250.     class function RecyclerQueryFunctionAvailable: boolean; static;
  251.  
  252.     class function RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean=false): boolean; static;
  253.   end;
  254.  
  255. function GPBoolToString(value: GPOLICYBOOL): string;
  256.  
  257. implementation
  258.  
  259. uses
  260.   RecBinUnitLowLvl;
  261.  
  262. {$REGION 'WinAPI/RTL declarations'}
  263. (*
  264. const
  265.   {$IFDEF MSWINDOWS}
  266.     shell32  = 'shell32.dll';
  267.     advapi32 = 'advapi32.dll';
  268.     kernel32 = 'kernel32.dll';
  269.   {$ENDIF}
  270.   {$IFDEF LINUX}
  271.     shell32  = 'libshell32.borland.so';
  272.     advapi32 = 'libwine.borland.so';
  273.     kernel32 = 'libwine.borland.so';
  274.   {$ENDIF}
  275. *)
  276.  
  277. type
  278.   SHELLSTATE = record
  279.     Flags1: DWORD;
  280. (*
  281.     BOOL fShowAllObjects : 1;
  282.     BOOL fShowExtensions : 1;
  283.     BOOL fNoConfirmRecycle : 1;
  284.  
  285.     BOOL fShowSysFiles : 1;
  286.     BOOL fShowCompColor : 1;
  287.     BOOL fDoubleClickInWebView : 1;
  288.     BOOL fDesktopHTML : 1;
  289.     BOOL fWin95Classic : 1;
  290.     BOOL fDontPrettyPath : 1;
  291.     BOOL fShowAttribCol : 1; // No longer used, dead bit
  292.     BOOL fMapNetDrvBtn : 1;
  293.     BOOL fShowInfoTip : 1;
  294.     BOOL fHideIcons : 1;
  295.     BOOL fWebView : 1;
  296.     BOOL fFilter : 1;
  297.     BOOL fShowSuperHidden : 1;
  298.     BOOL fNoNetCrawling : 1;
  299. *)
  300.     dwWin95Unused: DWORD; // Win95 only - no longer supported pszHiddenFileExts
  301.     uWin95Unused: UINT; // Win95 only - no longer supported cbHiddenFileExts
  302.  
  303.     // Note: Not a typo!  This is a persisted structure so we cannot use LPARAM
  304.     lParamSort: Integer;
  305.     iSortDirection: Integer;
  306.  
  307.     version: UINT;
  308.  
  309.     // new for win2k. need notUsed var to calc the right size of ie4 struct
  310.     // FIELD_OFFSET does not work on bit fields
  311.     uNotUsed: UINT; // feel free to rename and use
  312.     Flags2: DWORD;
  313. (*
  314.     BOOL fSepProcess: 1;
  315.     // new for Whistler.
  316.     BOOL fStartPanelOn: 1;       //Indicates if the Whistler StartPanel mode is ON or OFF.
  317.     BOOL fShowStartPage: 1;      //Indicates if the Whistler StartPage on desktop is ON or OFF.
  318.     UINT fSpareFlags : 13;
  319. *)
  320.   end;
  321.   LPSHELLSTATE = ^SHELLSTATE;
  322.  
  323. const
  324.   // Masks for the SHELLSTATE
  325.   SSF_SHOWALLOBJECTS       = $00000001;
  326.   SSF_SHOWEXTENSIONS       = $00000002;
  327.   SSF_HIDDENFILEEXTS       = $00000004;
  328.   SSF_SERVERADMINUI        = $00000004;
  329.   SSF_SHOWCOMPCOLOR        = $00000008;
  330.   SSF_SORTCOLUMNS          = $00000010;
  331.   SSF_SHOWSYSFILES         = $00000020;
  332.   SSF_DOUBLECLICKINWEBVIEW = $00000080;
  333.   SSF_SHOWATTRIBCOL        = $00000100;
  334.   SSF_DESKTOPHTML          = $00000200;
  335.   SSF_WIN95CLASSIC         = $00000400;
  336.   SSF_DONTPRETTYPATH       = $00000800;
  337.   SSF_SHOWINFOTIP          = $00002000;
  338.   SSF_MAPNETDRVBUTTON      = $00001000;
  339.   SSF_NOCONFIRMRECYCLE     = $00008000;
  340.   SSF_HIDEICONS            = $00004000;
  341.   SSF_FILTER               = $00010000;
  342.   SSF_WEBVIEW              = $00020000;
  343.   SSF_SHOWSUPERHIDDEN      = $00040000;
  344.   SSF_SEPPROCESS           = $00080000;
  345.   SSF_NONETCRAWLING        = $00100000;
  346.   SSF_STARTPANELON         = $00200000;
  347.   SSF_SHOWSTARTPAGE        = $00400000;
  348. {$ENDREGION}
  349.  
  350. resourcestring
  351.   LNG_API_CALL_ERROR = 'Error while calling the API. Additional information: "%s".';
  352.   LNG_NOT_CALLABLE = '%s not callable';
  353.   LNG_ERROR_CODE = '%s (Arguments: %s) returns error code %s';
  354.   LNG_FILE_NOT_FOUND = 'File not found: %s';
  355.   LNG_INVALID_INFO_FORMAT = 'Unexpected record size: %s';
  356.   LNG_DRIVE_NOT_EXISTING = 'Drive %s does not exist.';
  357.  
  358. const
  359.   C_SHQueryRecycleBin = 'SHQueryRecycleBinA';
  360.   C_GetVolumeNameForVolumeMountPoint = 'GetVolumeNameForVolumeMountPointA';
  361.   C_SHEmptyRecycleBinA = 'SHEmptyRecycleBinA';
  362.   C_SHGetSettings = 'SHGetSettings';
  363.   C_SHGetSetSettings = 'SHGetSetSettings';
  364.  
  365. type
  366.   TSHQueryRecycleBin = function(pszRootPath: LPCTSTR; var pSHQueryRBInfo: TSHQueryRBInfo): HRESULT; stdcall;
  367.   TGetVolumeNameForVolumeMountPointA = function(lpszVolumeMountPoint: LPCSTR; lpszVolumeName: LPSTR; cchBufferLength: DWORD): BOOL; stdcall;
  368.   TSHEmptyRecycleBin = function(Wnd: HWND; pszRootPath: PChar; dwFlags: DWORD): HRESULT; stdcall;
  369.   TSHGetSettings = procedure(var lpss: SHELLSTATE; dwMask: DWORD); stdcall;
  370.   TSHGetSetSettings = procedure(var lpss: SHELLSTATE; dwMask: DWORD; bSet: BOOL); stdcall;
  371.  
  372. function WideCharArrayToWideString(x: array of WideChar): WideString;
  373. var
  374.   i: integer;
  375. begin
  376.   // In x86, the "cast" works with WideString.
  377.   // In x64, it does not work (outputs empty string, without compiler warning!)
  378.   // So, we created this fake-cast
  379.   SetLength(result, Length(x));
  380.   for i := 0 to Length(x)-1 do
  381.     result[i+1] := x[i];
  382. end;
  383.  
  384. function AnsiCharArrayToWideString(x: array of AnsiChar): WideString;
  385. var
  386.   i: integer;
  387. begin
  388.   SetLength(result, Length(x));
  389.   for i := 0 to Length(x)-1 do
  390.     result[i+1] := WideChar(x[i]);
  391. end;
  392.  
  393. procedure AnsiRemoveNulChars(var s: AnsiString);
  394. begin
  395.   while (Length(s) > 0) and (s[Length(s)] = #0) do
  396.     s := Copy(s, 1, Length(s)-1);
  397. end;
  398.  
  399. procedure UnicodeRemoveNulChars(var s: WideString);
  400. begin
  401.   while (Length(s) > 0) and (s[Length(s)] = #0) do
  402.     s := Copy(s, 1, Length(s)-1);
  403. end;
  404.  
  405. function GetDriveGUID(driveLetter: AnsiChar; var guid: TGUID): DWORD;
  406. var
  407.   Buffer: array[0..50] of AnsiChar;
  408.   x: string;
  409.   PGetVolumeNameForVolumeMountPointA: TGetVolumeNameForVolumeMountPointA;
  410.   RBHandle: THandle;
  411. begin
  412.   RBHandle := LoadLibrary(kernel32);
  413.   try
  414.     if RBHandle <> 0 then
  415.     begin
  416.       PGetVolumeNameForVolumeMountPointA := GetProcAddress(RBHandle, C_GetVolumeNameForVolumeMountPoint);
  417.       if not Assigned(@PGetVolumeNameForVolumeMountPointA) then
  418.       begin
  419.         result := GetLastError;
  420.         FreeLibrary(RBHandle);
  421.         RBHandle := 0;
  422.       end
  423.       else
  424.       begin
  425.         if PGetVolumeNameForVolumeMountPointA(PAnsiChar(AnsiString(driveLetter+':\')), Buffer, SizeOf(Buffer)) then
  426.         begin
  427.           x := string(buffer);
  428.           x := copy(x, 11, 38);
  429.           guid := StringToGUID(x);
  430.           result := ERROR_SUCCESS;
  431.         end
  432.         else
  433.           result := GetLastError;
  434.       end;
  435.     end
  436.     else result := GetLastError;
  437.   finally
  438.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  439.   end;
  440. end;
  441.  
  442. function FileTimeToDateTime(FileTime: FILETIME): TDateTime;
  443. {$IFDEF FILETIME_DELPHI_CODE}
  444. var
  445.   SystemTime: TSystemTime;
  446.   nowUTC: TDateTime;
  447.   gmtDifference: int64;
  448. begin
  449.   GetSystemTime(SystemTime);
  450.   with SystemTime do
  451.   begin
  452.     // http://www.delphipraxis.net/post340194.html#34019
  453.     nowUTC := EncodeDate(wYear, wMonth, wDay) +
  454.               EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
  455.   end;
  456.  
  457.   gmtDifference := datetimetounix(nowUTC) - datetimetounix(Now);
  458.  
  459.   // http://www.e-fense.com/helix/Docs/Recycler_Bin_Record_Reconstruction.pdf states:
  460.   // UnixTime = 0.0000001 * NTTime + 11644473600
  461.   // This is wrong! The correct formula is:
  462.   // UnixTime = 0.0000001 * NTTime - 11644473600 + c * 3600
  463.   // c = GMT-Difference (MEZ = 1) inclusive daylight saving time (+3600 seconds)
  464.   result := unixtodatetime(round(0.0000001 * int64(FileTime)) - 11644473600 - gmtDifference);
  465. {$ELSE}
  466. var
  467.   LocalTime: TFileTime;
  468.   DOSTime: Integer;
  469. begin
  470.   FileTimeToLocalFileTime(FileTime, LocalTime);
  471.   FileTimeToDosDateTime(LocalTime, LongRec(DOSTime).Hi, LongRec(DOSTime).Lo);
  472.   Result := FileDateToDateTime(DOSTime);
  473. {$ENDIF}
  474. end;
  475.  
  476. function DeleteDirectory(const Name: string): boolean;
  477. var
  478.   F: TSearchRec;
  479. begin
  480.   result := true;
  481.   if FindFirst(IncludeTrailingPathDelimiter(Name) + '*', faAnyFile, F) = 0 then
  482.   begin
  483.     try
  484.       repeat
  485.         if F.Attr and faDirectory <> 0 then
  486.         begin
  487.           if (F.Name <> '.') and (F.Name <> '..') then
  488.           begin
  489.             result := result and DeleteDirectory(IncludeTrailingPathDelimiter(Name) + F.Name);
  490.           end;
  491.         end
  492.         else
  493.         begin
  494.           if not DeleteFile(IncludeTrailingPathDelimiter(Name) + F.Name) then result := false;
  495.         end;
  496.       until FindNext(F) <> 0;
  497.     finally
  498.       FindClose(F);
  499.     end;
  500.     if not RemoveDir(Name) then result := false;
  501.   end;
  502. end;
  503.  
  504. function DriveLetterToDriveNumber(driveLetter: AnsiChar): integer;
  505. var
  506.   tmp: string;
  507. begin
  508.   tmp := LowerCase(string(driveLetter));
  509.   result := Ord(tmp[1])-Ord('a');
  510. end;
  511.  
  512. function GetStringFromDLL(filename: string; num: integer): string;
  513. const
  514.   // Source: http://www.eggheadcafe.com/forumarchives/vcmfc/sep2005/post23917443.asp
  515.   MAX_BUF = 4097; // OK?
  516. var
  517.   hLib: THandle;
  518.   buf: array[0..MAX_BUF] of char;
  519. begin
  520.   hLib := LoadLibrary(PChar(filename));
  521.   try
  522.     LoadString(hLib, num, buf, sizeof(buf));
  523.     result := buf;
  524.   finally
  525.     FreeLibrary(hLib);
  526.   end;
  527. end;
  528.  
  529. function ExpandEnvStr(const szInput: string): string;
  530. const
  531.   MAXSIZE = 32768; // laut PSDK sind 32k das Maximum
  532. begin
  533.   // Source: http://www.delphi-library.de/topic_Umgebungsvariable+in+einem+String+aufloesen_20516,0.html
  534.   SetLength(Result,MAXSIZE);
  535.   SetLength(Result,ExpandEnvironmentStrings(pchar(szInput),
  536.     @Result[1],length(Result))-1); //-1 um abschließendes #0 zu verwerfen
  537. end;
  538.  
  539. function DecodeReferenceString(s: string): string;
  540. var
  541.   dll, id, lang, cache: string;
  542.   sl, sl2: tstringlist;
  543. begin
  544.   // Beispiele
  545.   // Papierkorb                                                 -- Windows 95
  546.   // @C:\WINNT\system32\shell32.dll,-8964@1031,Papierkorb       -- Windows 2000
  547.  
  548.   if Copy(s, 1, 1) = '@' then
  549.   begin
  550.     // Referenz auf eine DLL
  551.     // @<dll>,-<id>[@<lang>][,<cache>]
  552.  
  553.     sl := TStringList.Create;
  554.     try
  555.       // '@' am Anfang entfernen
  556.       s := Copy(s, 2, length(s)-1);
  557.  
  558.       // Nach ',' auftrennen
  559.       // sl[0] --> dll
  560.       // sl[1] --> -id@lang
  561.       // sl[2] --> cache
  562.       sl.CommaText := s;
  563.  
  564.       if sl.Count > 2 then
  565.       begin
  566.         // Das Ergebnis ist bereits im Klartext vorhanden und muss nicht extrahiert werden
  567.         // Ist bei Windows 2000 der Fall
  568.         cache := sl[2];
  569.         result := cache;
  570.         exit;
  571.       end;
  572.  
  573.       if sl.Count > 1 then
  574.       begin
  575.         dll := sl[0];
  576.  
  577.         sl2 := TStringList.Create;
  578.         try
  579.           // Nach '@' auftrennen
  580.           // sl2[0] --> id
  581.           // sl2[1] --> lang
  582.           sl2.CommaText := StringReplace(sl[1], '@', ',', [rfReplaceAll]);
  583.  
  584.           id := sl2[0];
  585.  
  586.           if sl2.Count > 1 then
  587.           begin
  588.             // ToDo: In Zukunft beachten, sofern möglich
  589.             lang := sl2[1];
  590.           end;
  591.  
  592.           // Umgebungsvariablen erkennen und Minuszeichen entfernen
  593.           result := GetStringFromDLL(ExpandEnvStr(dll), -StrToInt(id));
  594.         finally
  595.           sl2.Free;
  596.         end;
  597.       end
  598.       else
  599.       begin
  600.         // Zu wenige Informationen!
  601.  
  602.         result := '';
  603.       end;
  604.     finally
  605.       sl.Free;
  606.     end;
  607.   end
  608.   else
  609.   begin
  610.     // Kein Hinweis auf eine Referenz
  611.     result := s;
  612.   end;
  613. end;
  614.  
  615. function GPBoolToString(value: GPOLICYBOOL): string;
  616. begin
  617.   case value of
  618.     gpUndefined: result := 'Not configured';
  619.     gpEnabled: result := 'Enabled';
  620.     gpDisabled: result := 'Disabled';
  621.   end;
  622. end;
  623.  
  624. { TRbRecycleBin }
  625.  
  626. constructor TRbRecycleBin.Create(AFileOrDirectory: string; ASID: string='');
  627. begin
  628.   inherited Create;
  629.  
  630.   FFileOrDirectory := AFileOrDirectory;
  631.   FSID := ASID;
  632.   TolerantReading := true;
  633. end;
  634.  
  635. // TODO: also a function that tests if the data files are still existing
  636. function TRbRecycleBin.CheckIndexes(slErrors: TStrings): boolean;
  637.  
  638.   procedure _Assert(assertion: boolean; msg: string; args: array of const);
  639.   begin
  640.     if not assertion then
  641.     begin
  642.       slErrors.Add(Format(msg, args));
  643.       result := false;
  644.     end;
  645.   end;
  646.  
  647.   procedure _HandleIndexFile(AFile: string);
  648.   var
  649.     fs: TFileStream;
  650.     infoHdr: TRbInfoHeader;
  651.   resourcestring
  652.     LNG_IDXERR_VISTA_FILESIZE = '%s: Vista index file has wrong size';
  653.     LNG_IDXERR_INFO_RECSIZE_UNEXPECTED = '%s: record size unexpected';
  654.     LNG_IDXERR_INFO_UNEXPECTED_EOF = '%s: file size wrong';
  655.   begin
  656.     fs := TFileStream.Create(AFile, fmOpenRead);
  657.     try
  658.       fs.Seek(0, soFromBeginning);
  659.  
  660.       if SameText(copy(ExtractFileName(AFile), 1, 2), '$I') then
  661.       begin
  662.         _Assert(fs.Size = SizeOf(TRbVistaItem), LNG_IDXERR_VISTA_FILESIZE, [AFile]);
  663.       end
  664.       else if SameText(ExtractFileName(AFile), 'INFO') or
  665.               SameText(ExtractFileName(AFile), 'INFO2') then
  666.       begin
  667.         fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  668.         _Assert((infoHdr.recordLength = SizeOf(TRbInfoRecordA)) or
  669.                 (infoHdr.recordLength = SizeOf(TRbInfoRecordW)), LNG_IDXERR_INFO_RECSIZE_UNEXPECTED, [AFile]);
  670.         _Assert((fs.Size-fs.Position) mod infoHdr.recordLength = 0, LNG_IDXERR_INFO_UNEXPECTED_EOF, [AFile]);
  671.         // TODO: we can also check infoHdr.totalSize or infoHdr.totalEntries
  672.       end
  673.       else Assert(false);
  674.  
  675.       // TODO: we could check each item for invalid stuff...?
  676.     finally
  677.       FreeAndNil(fs);
  678.     end;
  679.   end;
  680.  
  681.   procedure _HandleVistaDir(ADirectory: string);
  682.   var
  683.     SR: TSearchRec;
  684.   begin
  685.     ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  686.  
  687.     if FindFirst(ADirectory + '$I*', faAnyFile, SR) = 0 then
  688.     begin
  689.       repeat
  690.         _HandleIndexFile(ADirectory+sr.Name);
  691.       until FindNext(SR) <> 0;
  692.     end;
  693.     FindClose(SR);
  694.   end;
  695.  
  696. begin
  697.   result := true;
  698.  
  699.   if DirectoryExists(FFileOrDirectory) then // Vista, as well as directories with INFO and INFO2
  700.   begin
  701.     _HandleVistaDir(FFileOrDirectory);
  702.  
  703.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2') then
  704.     begin
  705.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2');
  706.     end;
  707.  
  708.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO') then
  709.     begin
  710.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO');
  711.     end;
  712.   end
  713.   else if FileExists(FFileOrDirectory) then
  714.   begin
  715.     _HandleIndexFile(FFileOrDirectory);
  716.   end
  717.   else raise Exception.CreateFmt(LNG_FILE_NOT_FOUND, [FFileOrDirectory]);
  718. end;
  719.  
  720. function TRbRecycleBin.GetItem(id: string): TRbRecycleBinItem;
  721.  
  722.   procedure _HandleIndexFile(AFile: string);
  723.   var
  724.     fs: TFileStream;
  725.     infoHdr: TRbInfoHeader;
  726.     testItem: TRbRecycleBinItem;
  727.   begin
  728.     fs := TFileStream.Create(AFile, fmOpenRead);
  729.     try
  730.       fs.Seek(0, soFromBeginning);
  731.  
  732.       if SameText(ExtractFileName(AFile), '$I'+id) then
  733.       begin
  734.         result := TRbVistaItem.Create(fs, AFile, id);
  735.       end
  736.       else
  737.       begin
  738.         fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  739.         case infoHdr.recordLength of
  740.           SizeOf(TRbInfoRecordA):
  741.           begin
  742.             while fs.Position < fs.size do
  743.             begin
  744.               testItem := TRbInfoAItem.Create(fs, AFile);
  745.               if testItem.ID = id then
  746.               begin
  747.                 result := testItem;
  748.                 break;
  749.               end;
  750.             end;
  751.           end;
  752.           SizeOf(TRbInfoRecordW):
  753.           begin
  754.             while fs.Position < fs.size do
  755.             begin
  756.               testItem := TRbInfoWItem.Create(fs, AFile);
  757.               if testItem.ID = id then
  758.               begin
  759.                 result := testItem;
  760.                 break;
  761.               end;
  762.             end;
  763.           end
  764.           else
  765.           begin
  766.             raise Exception.CreateFmt(LNG_INVALID_INFO_FORMAT, [AFile]);
  767.           end;
  768.         end;
  769.       end;
  770.     finally
  771.       FreeAndNil(fs);
  772.     end;
  773.   end;
  774.  
  775.   procedure _HandleVistaDir(ADirectory: string);
  776.   var
  777.     SR: TSearchRec;
  778.     fs: TFileStream;
  779.     id: string;
  780.   begin
  781.     ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  782.  
  783.     if FileExists(ADirectory + '$I' + id) then
  784.     begin
  785.       fs := TFileStream.Create(ADirectory+sr.Name, fmOpenRead);
  786.       try
  787.         fs.Seek(0, soFromBeginning);
  788.         result := TRbVistaItem.Create(fs, ADirectory+sr.Name, id);
  789.       finally
  790.         FreeAndNil(fs);
  791.       end;
  792.     end;
  793.   end;
  794.  
  795. begin
  796.   result := nil;
  797.  
  798.   if DirectoryExists(FFileOrDirectory) then // Vista, as well as directories with INFO and INFO2
  799.   begin
  800.     _HandleVistaDir(FFileOrDirectory);
  801.     if Assigned(result) then exit;
  802.  
  803.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2') then
  804.     begin
  805.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2');
  806.       if Assigned(result) then exit;
  807.     end;
  808.  
  809.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO') then
  810.     begin
  811.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO');
  812.       if Assigned(result) then exit;
  813.     end;
  814.   end
  815.   else if FileExists(FFileOrDirectory) then
  816.   begin
  817.     _HandleIndexFile(FFileOrDirectory);
  818.     if Assigned(result) then exit;
  819.   end
  820.   else raise Exception.CreateFmt(LNG_FILE_NOT_FOUND, [FFileOrDirectory]);
  821. end;
  822.  
  823. procedure TRbRecycleBin.ListItems(list: TObjectList{TRbRecycleBinItem});
  824.  
  825.   procedure _HandleIndexFile(AFile: string);
  826.   var
  827.     fs: TFileStream;
  828.     infoHdr: TRbInfoHeader;
  829.     testID: string;
  830.     wTest: TRbInfoWItem;
  831.     bakPosition: int64;
  832.   begin
  833.     fs := TFileStream.Create(AFile, fmOpenRead);
  834.     try
  835.       fs.Seek(0, soFromBeginning);
  836.  
  837.       if SameText(copy(ExtractFileName(AFile), 1, 2), '$I') then
  838.       begin
  839.         testID := copy(testID, 3, Length(testID)-2);
  840.         list.Add(TRbVistaItem.Create(fs, AFile, testID));
  841.       end
  842.       else
  843.       begin
  844.         if TolerantReading then
  845.         begin
  846.           // This is a special treatment how to recover data from an INFO/INFO2 file
  847.           // which was corrupted by an incompatible multiboot configuration.
  848.           // Example:
  849.           // - Win95 without IE4 and WinNT4 both write into the INFO file. But Win95 appends the ANSI record and WinNT appends an Unicode record.
  850.           // - Win95 with IE4 and Windows 2000/2003/XP write into the INFO2 file. But Win9x appends the ANSI record and Win2k+ appends an Unicode record.
  851.           fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  852.           while fs.Position < fs.size do
  853.           begin
  854.             // Can we actually read a Unicode record?
  855.             if fs.Position + SizeOf(TRbInfoRecordW) <= fs.Size then
  856.             begin
  857.               // Try to read the Unicode record and check if it is valid
  858.               // In case it is no Unicode record, then the Unicode part will be the
  859.               // ANSI source name of the next record. In this case, we won't get
  860.               // a ':' at the Unicode string.
  861.               bakPosition := fs.Position;
  862.               wTest := TRbInfoWItem.Create(fs, AFile);
  863.               if Copy(wTest.SourceUnicode, 2, 1) = ':' then
  864.               begin
  865.                 // Yes, it is a valid Unicode record.
  866.                 list.Add(wTest);
  867.               end
  868.               else
  869.               begin
  870.                 // No, it is not a valid Unicode record. Jump back, and we need
  871.                 // to assume that the following record will be a valid ANSI record.
  872.                 fs.Position := bakPosition;
  873.                 list.Add(TRbInfoAItem.Create(fs, AFile));
  874.               end;
  875.             end
  876.             else
  877.             begin
  878.               // No, there is not enough space left for an Unicode record.
  879.               // So we assume that the following record will be a valid ANSI record.
  880.               list.Add(TRbInfoAItem.Create(fs, AFile));
  881.             end;
  882.           end;
  883.         end
  884.         else
  885.         begin
  886.           // This is the code for non-tolerant reading of the records.
  887.           fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  888.           case infoHdr.recordLength of
  889.             SizeOf(TRbInfoRecordA):
  890.             begin
  891.               while fs.Position < fs.size do
  892.               begin
  893.                 list.Add(TRbInfoAItem.Create(fs, AFile));
  894.               end;
  895.             end;
  896.             SizeOf(TRbInfoRecordW):
  897.             begin
  898.               while fs.Position < fs.size do
  899.               begin
  900.                 list.Add(TRbInfoWItem.Create(fs, AFile));
  901.               end;
  902.             end
  903.             else
  904.             begin
  905.               raise Exception.CreateFmt(LNG_INVALID_INFO_FORMAT, [AFile]);
  906.             end;
  907.           end;
  908.         end;
  909.       end;
  910.     finally
  911.       FreeAndNil(fs);
  912.     end;
  913.   end;
  914.  
  915.   procedure _HandleVistaDir(ADirectory: string);
  916.   var
  917.     SR: TSearchRec;
  918.     fs: TFileStream;
  919.     id: string;
  920.   begin
  921.     ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  922.  
  923.     if FindFirst(ADirectory + '$I*', faAnyFile, SR) = 0 then
  924.     begin
  925.       repeat
  926.         id := sr.Name;
  927.         { id := ChangeFileExt(id, ''); }  // Removed code: We keep the file extention as part of the ID, because we do not know if the ID is otherwise unique
  928.         id := Copy(id, 3, Length(id)-2);
  929.  
  930.         fs := TFileStream.Create(ADirectory+sr.Name, fmOpenRead);
  931.         try
  932.           fs.Seek(0, soFromBeginning);
  933.           list.Add(TRbVistaItem.Create(fs, ADirectory+sr.Name, id));
  934.         finally
  935.           FreeAndNil(fs);
  936.         end;
  937.       until FindNext(SR) <> 0;
  938.     end;
  939.     FindClose(SR);
  940.   end;
  941.  
  942. begin
  943.   if DirectoryExists(FFileOrDirectory) then // Vista, as well as directories with INFO and INFO2
  944.   begin
  945.     _HandleVistaDir(FFileOrDirectory);
  946.  
  947.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2') then
  948.     begin
  949.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2');
  950.     end;
  951.  
  952.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO') then
  953.     begin
  954.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO');
  955.     end;
  956.   end
  957.   else if FileExists(FFileOrDirectory) then
  958.   begin
  959.     _HandleIndexFile(FFileOrDirectory); // Either INFO, or INFO2, or a single Vista index file
  960.   end
  961.   else raise Exception.CreateFmt(LNG_FILE_NOT_FOUND, [FFileOrDirectory]);
  962. end;
  963.  
  964. { TRbDrive }
  965.  
  966. procedure TRbDrive.CheckDriveExisting;
  967. begin
  968.   // Does the drive exist?
  969.   // see http://www.delphipraxis.net/post2933.html
  970.   if not GetLogicalDrives and (1 shl DriveNumber) <> 0 then
  971.   begin
  972.     raise EInvalidDrive.CreateFmt(LNG_DRIVE_NOT_EXISTING, [UpperCase(string(FDriveLetter))+':']);
  973.   end;
  974. end;
  975.  
  976. constructor TRbDrive.Create(ADriveLetter: AnsiChar);
  977. begin
  978.   inherited Create;
  979.  
  980.   FDriveLetter := ADriveLetter;
  981.   CheckDriveExisting;
  982. end;
  983.  
  984. function TRbDrive.DiskSize: integer;
  985. begin
  986.   result := SysUtils.DiskSize(DriveNumber+1 {0 is current, 1 is A}) div (1024*1024);
  987. end;
  988.  
  989. function TRbDrive.DriveNumber: integer;
  990. begin
  991.   result := DriveLetterToDriveNumber(FDriveLetter);
  992. end;
  993.  
  994. function TRbDrive.GetAPIInfo: TSHQueryRBInfo;
  995. var
  996.   PSHQueryRecycleBin: TSHQueryRecycleBin;
  997.   RBHandle: THandle;
  998.   res: HRESULT;
  999.   Path: string;
  1000. begin
  1001.   Path := FDriveLetter + ':\';
  1002.  
  1003.   // Ref: http://www.delphipraxis.net/post1291.html
  1004.  
  1005.   RBHandle := LoadLibrary(shell32);
  1006.   try
  1007.     PSHQueryRecycleBin := nil;
  1008.     if RBHandle <> 0 then
  1009.     begin
  1010.       PSHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  1011.       if not Assigned(@PSHQueryRecycleBin) then
  1012.       begin
  1013.         FreeLibrary(RBHandle);
  1014.         RBHandle := 0;
  1015.       end;
  1016.     end;
  1017.  
  1018.     FillChar(result, SizeOf(TSHQueryRBInfo), 0);
  1019.     result.cbSize := SizeOf(TSHQueryRBInfo);
  1020.  
  1021.     if (RBHandle <> 0) and Assigned(PSHQueryRecycleBin) then
  1022.     begin
  1023.       res := PSHQueryRecycleBin(PChar(Path), result);
  1024.       // if Succeeded(res) then
  1025.       if res = S_OK then
  1026.       begin
  1027.         // Alles OK, unser result hat nun die gewünschten Daten.
  1028.       end
  1029.       else
  1030.       begin
  1031.         // Since Windows Vista, SHQueryRecycleBin will fail with E_FAIL (80004005)
  1032.         // if Path is a floppy or CD drive...
  1033.         raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_ERROR_CODE, [C_SHQueryRecycleBin, Path, '0x'+IntToHex(res, 2*SizeOf(HRESULT))])]);
  1034.       end;
  1035.     end
  1036.     else
  1037.       raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHQueryRecycleBin])]);
  1038.   finally
  1039.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  1040.   end;
  1041. end;
  1042.  
  1043. function TRbDrive.GetMaxPercentUsage: Extended;
  1044. var
  1045.   abs: integer; // in MB
  1046.   rel: integer; // in % (0-100)
  1047.   gpSetting: integer;
  1048. const
  1049.   DEFAULT_PERCENT = 10; // Windows 95 default
  1050. begin
  1051.   gpSetting := TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize;
  1052.   if gpSetting <> -1 then
  1053.     result := gpSetting / 100
  1054.   else if TRecycleBinManager.UsesGlobalSettings then
  1055.     result := TRecycleBinManager.GetGlobalMaxPercentUsage / 100
  1056.   else if OldCapacityPercent(rel) then
  1057.   begin
  1058.     result := rel / 100;
  1059.   end
  1060.   else if NewCapacityAbsolute(abs) then
  1061.   begin
  1062.     result := abs / DiskSize;
  1063.   end
  1064.   else
  1065.   begin
  1066.     result := DEFAULT_PERCENT / 100;
  1067.   end;
  1068. end;
  1069.  
  1070. function TRbDrive.GetMaxAbsoluteUsage: integer;
  1071. var
  1072.   abs: integer; // in MB
  1073.   rel: integer; // in % (0-100)
  1074.   gpSetting: integer;
  1075. const
  1076.   DEFAULT_PERCENT = 10; // Windows 95 default
  1077. begin
  1078.   gpSetting := TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize;
  1079.   if gpSetting <> -1 then
  1080.     result := Ceil(gpSetting/100 * DiskSize)
  1081.   else if TRecycleBinManager.UsesGlobalSettings then
  1082.     result := Ceil(TRecycleBinManager.GetGlobalMaxPercentUsage/100 * DiskSize)
  1083.   else if NewCapacityAbsolute(abs) then
  1084.   begin
  1085.     result := abs;
  1086.   end
  1087.   else if OldCapacityPercent(rel) then
  1088.   begin
  1089.     result := Ceil(rel/100 * DiskSize);
  1090.   end
  1091.   else
  1092.   begin
  1093.     result := Ceil(DEFAULT_PERCENT/100 * DiskSize);
  1094.   end;
  1095. end;
  1096.  
  1097. function TRbDrive.OldCapacityPercent(var res: integer): boolean;
  1098. var
  1099.   reg: TRegistry;
  1100.   purgeInfo: TRbWin95PurgeInfo;
  1101. begin
  1102.   if Win32MajorVersion >= 6 then
  1103.   begin
  1104.     // Only available till Windows XP
  1105.     result := false;
  1106.     exit;
  1107.   end;
  1108.  
  1109.   result := false;
  1110.  
  1111.   reg := TRegistry.Create;
  1112.   try
  1113.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1114.  
  1115.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1116.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1117.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1118.     begin
  1119.       if reg.OpenKeyReadOnly(string(FDriveLetter)) then
  1120.       begin
  1121.         if reg.ValueExists('Percent') then
  1122.         begin
  1123.           // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1124.  
  1125.           res := reg.ReadInteger('Percent');
  1126.           result := true;
  1127.         end;
  1128.       end
  1129.       else
  1130.       begin
  1131.         if reg.ValueExists('PurgeInfo') then
  1132.         begin
  1133.           // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1134.  
  1135.           reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1136.  
  1137.           res := purgeInfo.percentDrive[FDriveLetter];
  1138.           result := true;
  1139.         end;
  1140.       end;
  1141.  
  1142.       reg.CloseKey;
  1143.     end;
  1144.   finally
  1145.     reg.Free;
  1146.   end;
  1147. end;
  1148.  
  1149. function TRbDrive.NewCapacityAbsolute(var res: integer): boolean;
  1150. var
  1151.   reg: TRegistry;
  1152. begin
  1153.   if Win32MajorVersion < 6 then
  1154.   begin
  1155.     // Only available since Windows Vista
  1156.     result := false;
  1157.     exit;
  1158.   end;
  1159.  
  1160.   result := false;
  1161.  
  1162.   reg := TRegistry.Create;
  1163.   try
  1164.     reg.RootKey := HKEY_CURRENT_USER;
  1165.  
  1166.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume') then
  1167.     begin
  1168.       // Windows Vista and upwards
  1169.       if reg.OpenKeyReadOnly(GUIDToString(VolumeGUID)) then
  1170.       begin
  1171.         res := reg.ReadInteger('MaxCapacity'); // in MB
  1172.         result := true;
  1173.       end;
  1174.       reg.CloseKey;
  1175.     end;
  1176.   finally
  1177.     reg.Free;
  1178.   end;
  1179. end;
  1180.  
  1181. function TRbDrive.GetNukeOnDelete: boolean;
  1182. var
  1183.   reg: TRegistry;
  1184.   purgeInfo: TRbWin95PurgeInfo;
  1185. const
  1186.   RES_DEFAULT = false; // Windows 95 default
  1187. begin
  1188.   if TRecycleBinManager.RecyclerGroupPolicyNoRecycleFiles = gpEnabled then
  1189.     result := true
  1190.   else if TRecycleBinManager.UsesGlobalSettings then
  1191.     result := TRecycleBinManager.GetGlobalNukeOnDelete
  1192.   else
  1193.   begin
  1194.     result := RES_DEFAULT;
  1195.  
  1196.     reg := TRegistry.Create;
  1197.     try
  1198.       reg.RootKey := HKEY_CURRENT_USER;
  1199.  
  1200.       if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume') then
  1201.       begin
  1202.         // Windows Vista and upwards
  1203.         if reg.OpenKeyReadOnly(GUIDToString(VolumeGUID)) then
  1204.         begin
  1205.           result := reg.ReadBool('NukeOnDelete');
  1206.         end;
  1207.         reg.CloseKey;
  1208.       end
  1209.       else
  1210.       begin
  1211.         reg.RootKey := HKEY_LOCAL_MACHINE;
  1212.  
  1213.         // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1214.         // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1215.         if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1216.         begin
  1217.           if reg.OpenKeyReadOnly(string(FDriveLetter)) then
  1218.           begin
  1219.             if reg.ValueExists('NukeOnDelete') then
  1220.             begin
  1221.               // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1222.  
  1223.               result := reg.ReadBool('NukeOnDelete');
  1224.             end;
  1225.           end
  1226.           else
  1227.           begin
  1228.             if reg.ValueExists('PurgeInfo') then
  1229.             begin
  1230.               // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1231.  
  1232.               reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1233.  
  1234.               result := ((purgeInfo.NukeOnDeleteBits shr DriveNumber) and 1) = 1;
  1235.             end;
  1236.           end;
  1237.  
  1238.           reg.CloseKey;
  1239.         end;
  1240.       end;
  1241.     finally
  1242.       reg.Free;
  1243.     end;
  1244.   end;
  1245. end;
  1246.  
  1247. function TRbDrive.GetNumItems: int64;
  1248. begin
  1249.   result := GetAPIInfo.i64NumItems;
  1250. end;
  1251.  
  1252. function TRbDrive.GetSize: int64;
  1253. begin
  1254.   result := GetAPIInfo.i64Size;
  1255. end;
  1256.  
  1257. function TRbDrive.GetVolumeGUID: TGUID;
  1258. begin
  1259.   if GetDriveGUID(FDriveLetter, result) <> ERROR_SUCCESS then
  1260.   begin
  1261.     result := NULL_GUID;
  1262.   end;
  1263. end;
  1264.  
  1265. function TRbDrive.GetVolumeGUIDAvailable: boolean;
  1266. begin
  1267.   result := not IsEqualGUID(VolumeGUID, NULL_GUID);
  1268. end;
  1269.  
  1270. function TRbDrive.IsEmpty: boolean;
  1271. begin
  1272.   result := GetNumItems = 0;
  1273. end;
  1274.  
  1275. function TRbDrive.IsFAT: boolean;
  1276. var
  1277.   Dummy2: DWORD;
  1278.   Dummy3: DWORD;
  1279.   FileSystem: array[0..MAX_PATH] of char;
  1280.   VolumeName: array[0..MAX_PATH] of char;
  1281.   s: string;
  1282. begin
  1283.   s := FDriveLetter + DriveDelim + PathDelim; // ohne die Auslagerung in einen String kommt es zu einer AV in ntdll
  1284.   GetVolumeInformation(PChar(s), VolumeName,
  1285.     SizeOf(VolumeName), nil, Dummy2, Dummy3, FileSystem, SizeOf(FileSystem));
  1286.   result := uppercase(copy(FileSystem, 0, 3)) = 'FAT';
  1287. end;
  1288.  
  1289. procedure TRbDrive.ListRecycleBins(list: TObjectList{TRbRecycleBin}; UserSID: string='');
  1290.  
  1291.   procedure _AddSIDFolders(dir: string; wholeFolder: boolean);
  1292.   var
  1293.     SR: TSearchRec;
  1294.   begin
  1295.     dir := IncludeTrailingPathDelimiter(dir);
  1296.     if FindFirst(dir+'S-*', faAnyFile, SR) = 0 then
  1297.     begin
  1298.       try
  1299.         repeat
  1300.           if (SR.Name = '.') or (SR.Name = '..') or not DirectoryExists(dir + SR.Name) then continue;
  1301.  
  1302.           if wholeFolder then
  1303.           begin
  1304.             // Vista
  1305.             list.Add(TRbRecycleBin.Create(dir+SR.Name, SR.Name));
  1306.           end
  1307.           else
  1308.           begin
  1309.             // Win95 .. WinXP
  1310.             if FileExists(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO2') then
  1311.               list.Add(TRbRecycleBin.Create(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO2', SR.Name));
  1312.             if FileExists(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO') then
  1313.               list.Add(TRbRecycleBin.Create(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO', SR.Name));
  1314.           end;
  1315.         until FindNext(SR) <> 0;
  1316.       finally
  1317.         FindClose(SR);
  1318.       end;
  1319.     end;
  1320.   end;
  1321.  
  1322. var
  1323.   dir: string;
  1324. begin
  1325.   // Find recyclers from Windows Vista or higher
  1326.  
  1327.   if IsFAT then
  1328.   begin
  1329.     dir := FDriveLetter + DriveDelim + PathDelim + '$recycle.bin' + PathDelim;
  1330.     if DirectoryExists(dir) then
  1331.     begin
  1332.       list.Add(TRbRecycleBin.Create(dir));
  1333.     end;
  1334.   end
  1335.   else
  1336.   begin
  1337.     if UserSID <> '' then
  1338.     begin
  1339.       dir := FDriveLetter + DriveDelim + PathDelim + '$recycle.bin' + PathDelim + UserSID + PathDelim;
  1340.       if DirectoryExists(dir) then
  1341.       begin
  1342.         list.Add(TRbRecycleBin.Create(dir, UserSID));
  1343.       end;
  1344.     end
  1345.     else
  1346.     begin
  1347.       _AddSIDFolders(FDriveLetter + DriveDelim + PathDelim + '$recycle.bin', true);
  1348.     end;
  1349.   end;
  1350.  
  1351.   // Find recyclers from Windows before Vista
  1352.  
  1353.   if IsFAT then
  1354.   begin
  1355.     dir := FDriveLetter + DriveDelim + PathDelim + 'Recycled' + PathDelim;
  1356.  
  1357.     // Both "recycle bins" are possible if you have multiboot (but do overwrite themselfes if you empty them)
  1358.     if FileExists(dir + 'INFO2') then
  1359.       list.Add(TRbRecycleBin.Create(dir + 'INFO2')); // Windows 95 with Internet Explorer 4 Extension or higher Windows 9x versions
  1360.     if FileExists(dir + 'INFO') then
  1361.       list.Add(TRbRecycleBin.Create(dir + 'INFO')); // Windows 95 native
  1362.   end
  1363.   else
  1364.   begin
  1365.     if UserSID <> '' then
  1366.     begin
  1367.       dir := FDriveLetter + DriveDelim + PathDelim + 'Recycler' + PathDelim + UserSID + PathDelim;
  1368.  
  1369.       if FileExists(dir + 'INFO2') then
  1370.         list.Add(TRbRecycleBin.Create(dir + 'INFO2', UserSID)); // Windows 2000+
  1371.       if FileExists(dir + 'INFO') then
  1372.         list.Add(TRbRecycleBin.Create(dir + 'INFO', UserSID)); // Windows NT 4
  1373.     end
  1374.     else
  1375.     begin
  1376.       _AddSIDFolders(FDriveLetter + DriveDelim + PathDelim + 'Recycler', false);
  1377.     end;
  1378.   end;
  1379. end;
  1380.  
  1381. { TRbInfoAItem }
  1382.  
  1383. procedure TRbInfoAItem.ReadFromStream(stream: TStream);
  1384. var
  1385.   r: TRbInfoRecordA;
  1386. begin
  1387.   stream.ReadBuffer(r, SizeOf(r));
  1388.  
  1389.   FSourceDrive := Chr(Ord('A') + r.sourceDrive);
  1390.  
  1391.   // Win95 with IE4 and Win2000+:
  1392.   // Wenn ein Eintrag aus der INFO/INFO2 gelöscht wird, dann wird das erste Byte
  1393.   // von sourceAnsi auf Null gesetzt, damit die ganze INFO/INFO2 Datei nicht
  1394.   // ständig neu geschrieben werden muss (so wie es bei Win95 und WinNT4 der Fall war).
  1395.   // Wir lesen den Eintrag trotzdem, da unsere Software ja auch zu forensischen
  1396.   // Zwecken eingesetzt werden soll.
  1397.   if r.sourceAnsi[0] = #0 then
  1398.   begin
  1399.     FRemovedEntry := true;
  1400.     r.sourceAnsi[0] := AnsiChar(FSourceDrive);
  1401.   end;
  1402.  
  1403.   FSourceAnsi := r.sourceAnsi;
  1404.   FSourceUnicode := AnsiCharArrayToWideString(r.sourceAnsi); // Unicode does not exist in INFO(1) structure
  1405.   FID := IntToStr(r.recordNumber);
  1406.   FDeletionTime := FileTimeToDateTime(r.deletionTime);
  1407.   FOriginalSize := r.originalSize;
  1408.  
  1409.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1410.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1411.   AnsiRemoveNulChars(FSourceAnsi);
  1412.   UnicodeRemoveNulChars(FSourceUnicode);
  1413. end;
  1414.  
  1415. function TRbInfoAItem.DeleteFile: boolean;
  1416. var
  1417.   r: string;
  1418. begin
  1419.   r := GetPhysicalFile;
  1420.   if DirectoryExists(r) then
  1421.     result := DeleteDirectory(r) // Usually, the old recycle bin does not allow folders. Just to be sure, we include the code.
  1422.   else
  1423.     result := SysUtils.DeleteFile(r); // TODO: geht das oder gibt es zugriffsverletzung? --> Win95: Funktioniert
  1424.  
  1425.   // TODO: nun auch den eintrag aus der INFO-Datei rausschmeißen (Datei neu schreiben)
  1426. end;
  1427.  
  1428. function TRbInfoAItem.GetPhysicalFile: string;
  1429. begin
  1430.   if FRemovedEntry then
  1431.   begin
  1432.     result := '';
  1433.     Exit;
  1434.   end;
  1435.  
  1436.   // e.g. C:\...\DC0.doc
  1437.   result := IncludeTrailingPathDelimiter(ExtractFilePath(IndexFile)) +
  1438.             'D' + (* SourceDrive *) Source[1] + ID + ExtractFileExt(Source);
  1439. end;
  1440.  
  1441. constructor TRbInfoAItem.Create(fs: TStream; AIndexFile: string);
  1442. begin
  1443.   inherited Create;
  1444.   ReadFromStream(fs);
  1445.   FIndexFile := AIndexFile;
  1446. end;
  1447.  
  1448. { TRbInfoWItem }
  1449.  
  1450. procedure TRbInfoWItem.ReadFromStream(stream: TStream);
  1451. var
  1452.   r: TRbInfoRecordW;
  1453. begin
  1454.   stream.ReadBuffer(r, SizeOf(r));
  1455.  
  1456.   // Win95 with IE4 and Win2000+:
  1457.   // Wenn ein Eintrag aus der INFO/INFO2 gelöscht wird, dann wird das erste Byte
  1458.   // von sourceAnsi auf Null gesetzt, damit die ganze INFO/INFO2 Datei nicht
  1459.   // ständig neu geschrieben werden muss (so wie es bei Win95 und WinNT4 der Fall war).
  1460.   // Wir lesen den Eintrag trotzdem, da unsere Software ja auch zu forensischen
  1461.   // Zwecken eingesetzt werden soll.
  1462.   if r.sourceAnsi[0] = #0 then
  1463.   begin
  1464.     FRemovedEntry := true;
  1465.     r.sourceAnsi[0] := AnsiChar(r.sourceUnicode[0]);
  1466.   end;
  1467.  
  1468.   FSourceAnsi := r.sourceAnsi;
  1469.   FSourceUnicode := r.sourceUnicode;
  1470.   FID := IntToStr(r.recordNumber);
  1471.   FSourceDrive := Chr(Ord('A') + r.sourceDrive);
  1472.   FDeletionTime := FileTimeToDateTime(r.deletionTime);
  1473.   FOriginalSize := r.originalSize;
  1474.  
  1475.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1476.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1477.   AnsiRemoveNulChars(FSourceAnsi);
  1478.   UnicodeRemoveNulChars(FSourceUnicode);
  1479. end;
  1480.  
  1481. function TRbInfoWItem.DeleteFile: boolean;
  1482. var
  1483.   r: string;
  1484. begin
  1485.   r := GetPhysicalFile;
  1486.   if DirectoryExists(r) then
  1487.     result := DeleteDirectory(r)
  1488.   else
  1489.     result := SysUtils.DeleteFile(r); // TODO: geht das oder gibt es zugriffsverletzung?
  1490.  
  1491.   // TODO: nun auch den eintrag aus der INFO-Datei rausschmeißen (Erstes Byte auf 0 setzen)
  1492. end;
  1493.  
  1494. function TRbInfoWItem.GetPhysicalFile: string;
  1495. begin
  1496.   if FRemovedEntry then
  1497.   begin
  1498.     result := '';
  1499.     Exit;
  1500.   end;
  1501.  
  1502.   (*
  1503.   This is actually a bit tricky...
  1504.   Win95 will choose the first letter of the AnsiSource name.
  1505.   WinNT will choose the first letter of the UnicodeSource name.
  1506.   WinXP will choose the driveNumber member.
  1507.  
  1508.   Windows XP is kinda buggy when it comes to changing a drive letter.
  1509.   For example, the drive E: was changed to K:
  1510.   The drive letter is 04 (E), the Source name begins with E:\ and the physical file is De0.txt .
  1511.   After the recycle bin is opened the first time:
  1512.   - The recycle bin will show the file origin as K:\ and not as E:\
  1513.   - The file was renamed from De0.txt to Dk0.txt
  1514.   - The file can be recovered at this time
  1515.   When the recycle bin is closed, the INFO2 file will not be corrected (which is a bug).
  1516.   So, if you open the recycle bin again, the record will be marked
  1517.   as deleted in the INFO file (the first byte will be set to 0),
  1518.   because Windows searches for De0.txt and doesn't find it.
  1519.  
  1520.   (This comment also applies to TRbInfoAItem.GetPhysicalFile)
  1521.   *)
  1522.  
  1523.   // e.g. C:\...\DC0.doc
  1524.   result := IncludeTrailingPathDelimiter(ExtractFilePath(IndexFile)) +
  1525.             'D' + SourceDrive (* SourceUnicode[1] *) + ID + ExtractFileExt(SourceUnicode);
  1526. end;
  1527.  
  1528. constructor TRbInfoWItem.Create(fs: TStream; AIndexFile: string);
  1529. begin
  1530.   inherited Create;
  1531.   ReadFromStream(fs);
  1532.   FIndexFile := AIndexFile;
  1533. end;
  1534.  
  1535. { TRbVistaItem }
  1536.  
  1537. procedure TRbVistaItem.ReadFromStream(stream: TStream);
  1538. var
  1539.   r1: TRbVistaRecord1;
  1540.   r2: TRbVistaRecord2Head;
  1541.   r2SourceUnicode: array of WideChar;
  1542.   version: DWORD;
  1543. begin
  1544.   stream.ReadBuffer(version, SizeOf(version));
  1545.  
  1546.   if version = 1 then
  1547.   begin
  1548.     stream.Seek(0, soBeginning);
  1549.     stream.ReadBuffer(r1, SizeOf(r1));
  1550.     FSourceAnsi := AnsiString(WideCharArrayToWideString(r1.sourceUnicode)); // Invalid chars are automatically converted into '?'
  1551.     FSourceUnicode := WideCharArrayToWideString(r1.sourceUnicode);
  1552.     FID := ''; // will be added manually (at the constructor)
  1553.     FSourceDrive := r1.sourceUnicode[1];
  1554.     FDeletionTime := FileTimeToDateTime(r1.deletionTime);
  1555.     FOriginalSize := r1.originalSize;
  1556.   end
  1557.   else if version = 2 then
  1558.   begin
  1559.     stream.Seek(0, soBeginning);
  1560.     stream.ReadBuffer(r2, SizeOf(r2));
  1561.  
  1562.     SetLength(r2SourceUnicode, SizeOf(WideChar)*(r2.SourceCountChars-1));
  1563.     stream.Read(r2SourceUnicode[0], SizeOf(WideChar)*(r2.sourceCountChars-1));
  1564.  
  1565.     FSourceAnsi := AnsiString(WideCharArrayToWideString(r2sourceUnicode)); // Invalid chars are automatically converted into '?'
  1566.     FSourceUnicode := WideCharArrayToWideString(r2sourceUnicode);
  1567.     FID := ''; // will be added manually (at the constructor)
  1568.     FSourceDrive := r2sourceUnicode[1];
  1569.     FDeletionTime := FileTimeToDateTime(r2.deletionTime);
  1570.     FOriginalSize := r2.originalSize;
  1571.   end
  1572.   else
  1573.   begin
  1574.     raise Exception.CreateFmt('Invalid Vista index format version %d', [version]);
  1575.   end;
  1576.  
  1577.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1578.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1579.   AnsiRemoveNulChars(FSourceAnsi);
  1580.   UnicodeRemoveNulChars(FSourceUnicode);
  1581. end;
  1582.  
  1583. function TRbVistaItem.DeleteFile: boolean;
  1584. var
  1585.   r: string;
  1586. begin
  1587.   r := GetPhysicalFile;
  1588.   if DirectoryExists(r) then
  1589.     result := DeleteDirectory(r)
  1590.   else
  1591.     result := SysUtils.DeleteFile(r);
  1592.  
  1593.   SysUtils.DeleteFile(FIndexFile);
  1594. end;
  1595.  
  1596. function TRbVistaItem.GetPhysicalFile: string;
  1597. begin
  1598.   result := FIndexFile;
  1599.   result := StringReplace(Result, '$I', '$R', [rfIgnoreCase]);
  1600. end;
  1601.  
  1602. constructor TRbVistaItem.Create(fs: TStream; AIndexFile, AID: string);
  1603. begin
  1604.   inherited Create;
  1605.   ReadFromStream(fs);
  1606.   FIndexFile := AIndexFile;
  1607.   FID := AID;
  1608. end;
  1609.  
  1610. { TRecycleBinManager }
  1611.  
  1612. class function TRecycleBinManager.EmptyOwnRecyclers(flags: cardinal): boolean;
  1613. var
  1614.   PSHEmptyRecycleBin: TSHEmptyRecycleBin;
  1615.   LibHandle: THandle;
  1616. begin
  1617.   // Source: http://www.dsdt.info/tipps/?id=176
  1618.   result := true;
  1619.   LibHandle := LoadLibrary(shell32);
  1620.   try
  1621.     if LibHandle <> 0 then
  1622.     begin
  1623.       @PSHEmptyRecycleBin := GetProcAddress(LibHandle, C_SHEmptyRecycleBinA);
  1624.       if @PSHEmptyRecycleBin <> nil then
  1625.       begin
  1626.         PSHEmptyRecycleBin(hInstance, nil, flags);
  1627.       end
  1628.       else
  1629.         result := false;
  1630.     end
  1631.     else
  1632.       result := false;
  1633.   finally
  1634.     @PSHEmptyRecycleBin := nil;
  1635.     if LibHandle <> 0 then FreeLibrary(LibHandle);
  1636.   end;
  1637. end;
  1638.  
  1639. class function TRecycleBinManager.EmptyOwnRecyclers(sound, progress, confirmation: boolean): boolean;
  1640. const
  1641.   SHERB_NOCONFIRMATION = $00000001;
  1642.   SHERB_NOPROGRESSUI   = $00000002;
  1643.   SHERB_NOSOUND        = $00000004;
  1644. var
  1645.   flags: cardinal;
  1646. begin
  1647.   flags := 0;
  1648.  
  1649.   if not progress then
  1650.     flags := flags or SHERB_NOPROGRESSUI;
  1651.   if not confirmation then
  1652.     flags := flags or SHERB_NOCONFIRMATION;
  1653.   if not sound then
  1654.     flags := flags or SHERB_NOSOUND;
  1655.  
  1656.   result := EmptyOwnRecyclers(flags);
  1657. end;
  1658.  
  1659. class function TRecycleBinManager.GetGlobalMaxPercentUsage: integer;
  1660. var
  1661.   reg: TRegistry;
  1662.   purgeInfo: TRbWin95PurgeInfo;
  1663. const
  1664.   RES_DEFAULT = 10; // Windows 95 - Standardwert
  1665. begin
  1666.   if Win32MajorVersion >= 6 then
  1667.   begin
  1668.     // Only available till Windows XP
  1669.     result := -1;
  1670.     exit;
  1671.   end;
  1672.  
  1673.   result := RES_DEFAULT;
  1674.  
  1675.   reg := TRegistry.Create;
  1676.   try
  1677.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1678.  
  1679.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1680.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1681.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1682.     begin
  1683.       if reg.ValueExists('Percent') then
  1684.       begin
  1685.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1686.  
  1687.         result := reg.ReadInteger('Percent');
  1688.       end
  1689.       else if reg.ValueExists('PurgeInfo') then
  1690.       begin
  1691.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1692.  
  1693.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1694.         result := purgeInfo.percentGlobal;
  1695.       end;
  1696.  
  1697.       reg.CloseKey;
  1698.     end;
  1699.   finally
  1700.     reg.Free;
  1701.   end;
  1702. end;
  1703.  
  1704. class function TRecycleBinManager.GetGlobalNukeOnDelete: boolean;
  1705. var
  1706.   reg: TRegistry;
  1707.   purgeInfo: TRbWin95PurgeInfo;
  1708. const
  1709.   RES_DEFAULT = false; // Windows 95 - Standardwert
  1710. begin
  1711.   if Win32MajorVersion >= 6 then
  1712.   begin
  1713.     // Only available till Windows XP
  1714.     result := false;
  1715.     exit;
  1716.   end;
  1717.  
  1718.   result := RES_DEFAULT;
  1719.  
  1720.   reg := TRegistry.Create;
  1721.   try
  1722.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1723.  
  1724.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1725.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1726.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1727.     begin
  1728.       if reg.ValueExists('NukeOnDelete') then
  1729.       begin
  1730.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1731.  
  1732.         result := reg.ReadBool('NukeOnDelete');
  1733.       end
  1734.       else if reg.ValueExists('PurgeInfo') then
  1735.       begin
  1736.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1737.  
  1738.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1739.         result := (purgeInfo.NukeOnDeleteBits and $8000000) = $8000000; // bit 27
  1740.       end;
  1741.  
  1742.       reg.CloseKey;
  1743.     end;
  1744.   finally
  1745.     reg.Free;
  1746.   end;
  1747. end;
  1748.  
  1749. (* TODO:
  1750. There are more registry values (found in WinXP):
  1751.  
  1752. BitBucket\<driveletter>
  1753.   VolumeSerialNumber
  1754.   IsUnicode
  1755.  
  1756. *)
  1757.  
  1758. class function TRecycleBinManager.UsesGlobalSettings: boolean;
  1759. var
  1760.   reg: TRegistry;
  1761.   purgeInfo: TRbWin95PurgeInfo;
  1762. const
  1763.   RES_DEFAULT = true; // Windows 95 - Standardwert
  1764. begin
  1765.   if Win32MajorVersion >= 6 then
  1766.   begin
  1767.     // Only available till Windows XP
  1768.     result := false;
  1769.     exit;
  1770.   end;
  1771.  
  1772.   result := RES_DEFAULT;
  1773.  
  1774.   reg := TRegistry.Create;
  1775.   try
  1776.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1777.  
  1778.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1779.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1780.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1781.     begin
  1782.       if reg.ValueExists('UseGlobalSettings') then
  1783.       begin
  1784.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1785.  
  1786.         result := reg.ReadBool('UseGlobalSettings');
  1787.       end
  1788.       else if reg.ValueExists('PurgeInfo') then
  1789.       begin
  1790.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1791.  
  1792.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1793.         result := purgeInfo.bGlobalSettings;
  1794.       end;
  1795.  
  1796.       reg.CloseKey;
  1797.     end;
  1798.   finally
  1799.     reg.Free;
  1800.   end;
  1801. end;
  1802.  
  1803. class procedure TRecycleBinManager.ListDrives(list: TObjectList{TRbDrive});
  1804. var
  1805.   drive: AnsiChar;
  1806. begin
  1807.   for drive := 'A' to 'Z' do
  1808.     if RecycleBinPossible(drive) then
  1809.       list.Add(TRbDrive.Create(drive));
  1810. end;
  1811.  
  1812. class function TRecycleBinManager.OwnRecyclersEmpty: boolean;
  1813. var
  1814.   drives: TObjectList;
  1815.   i: integer;
  1816. begin
  1817.   result := true;
  1818.  
  1819.   drives := TObjectList.Create(true);
  1820.   try
  1821.     ListDrives(drives);
  1822.     for i := 0 to drives.Count - 1 do
  1823.     begin
  1824.       result := result and TRbDrive(drives.Items[i]).IsEmpty;
  1825.       if not result then break;
  1826.     end;
  1827.   finally
  1828.     drives.Free;
  1829.   end;
  1830. end;
  1831.  
  1832. class function TRecycleBinManager.OwnRecyclersNumItems: int64;
  1833. var
  1834.   drives: TObjectList;
  1835.   i: integer;
  1836. begin
  1837.   result := 0;
  1838.  
  1839.   drives := TObjectList.Create(true);
  1840.   try
  1841.     ListDrives(drives);
  1842.     for i := 0 to drives.Count - 1 do
  1843.     begin
  1844.       result := result + TRbDrive(drives.Items[i]).GetNumItems;
  1845.     end;
  1846.   finally
  1847.     drives.Free;
  1848.   end;
  1849. end;
  1850.  
  1851. class function TRecycleBinManager.OwnRecyclersSize: int64;
  1852. var
  1853.   drives: TObjectList;
  1854.   i: integer;
  1855. begin
  1856.   result := 0;
  1857.  
  1858.   drives := TObjectList.Create(true);
  1859.   try
  1860.     ListDrives(drives);
  1861.     for i := 0 to drives.Count - 1 do
  1862.     begin
  1863.       result := result + TRbDrive(drives.Items[i]).GetSize;
  1864.     end;
  1865.   finally
  1866.     drives.Free;
  1867.   end;
  1868. end;
  1869.  
  1870. class function TRecycleBinManager.RecycleBinPossible(Drive: AnsiChar): boolean;
  1871. var
  1872.   typ: Integer;
  1873. begin
  1874.   // Does the drive exist?
  1875.   // see http://www.delphipraxis.net/post2933.html
  1876.   result := GetLogicalDrives and (1 shl DriveLetterToDriveNumber(Drive)) <> 0;
  1877.   if not result then exit;
  1878.  
  1879.   // Is it a fixed drive? (Only they can have recycle bins)
  1880.   // TODO: is that correct, or can also have other drive types have recyclers?
  1881.   typ := GetDriveType(PChar(Drive + ':\'));
  1882.   result := typ = DRIVE_FIXED;
  1883. end;
  1884.  
  1885. class function TRecycleBinManager.RecyclerGetCurrentIconString: string;
  1886. begin
  1887.   if OwnRecyclersEmpty then
  1888.     result := RecyclerGetEmptyIconString
  1889.   else
  1890.     result := RecyclerGetFullIconString;
  1891. end;
  1892.  
  1893. class function TRecycleBinManager.RecyclerGetDefaultIconString: string;
  1894. var
  1895.   reg: TRegistry;
  1896. begin
  1897.   // Please note: The "default" icon is not always the icon of the
  1898.   // current recycle bin in its current state (full, empty)
  1899.   // At Windows 95b, the registry value actually did change every time the
  1900.   // recycle bin state did change, but at Windows 2000 I could not see any
  1901.   // update, even after reboot. So, the registry value is possible fixed as
  1902.   // default = empty on newer OS versions.
  1903.  
  1904.   reg := TRegistry.Create;
  1905.   try
  1906.     reg.RootKey := HKEY_CLASSES_ROOT;
  1907.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1908.     begin
  1909.       result := reg.ReadString('');
  1910.       reg.CloseKey;
  1911.     end;
  1912.   finally
  1913.     reg.Free;
  1914.   end;
  1915. end;
  1916.  
  1917. class function TRecycleBinManager.RecyclerGetEmptyIconString: string;
  1918. var
  1919.   reg: TRegistry;
  1920. begin
  1921.   reg := TRegistry.Create;
  1922.   try
  1923.     reg.RootKey := HKEY_CLASSES_ROOT;
  1924.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1925.     begin
  1926.       result := reg.ReadString('Empty');
  1927.       reg.CloseKey;
  1928.     end;
  1929.   finally
  1930.     reg.Free;
  1931.   end;
  1932. end;
  1933.  
  1934. class function TRecycleBinManager.RecyclerGetFullIconString: string;
  1935. var
  1936.   reg: TRegistry;
  1937. begin
  1938.   reg := TRegistry.Create;
  1939.   try
  1940.     reg.RootKey := HKEY_CLASSES_ROOT;
  1941.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1942.     begin
  1943.       result := reg.ReadString('Full');
  1944.       reg.CloseKey;
  1945.     end;
  1946.   finally
  1947.     reg.Free;
  1948.   end;
  1949. end;
  1950.  
  1951. class function TRecycleBinManager.RecyclerGetInfoTip: string;
  1952. var
  1953.   reg: TRegistry;
  1954. begin
  1955.   // Not available in some older versions of Windows
  1956.  
  1957.   reg := TRegistry.Create;
  1958.   try
  1959.     reg.RootKey := HKEY_CLASSES_ROOT;
  1960.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  1961.     begin
  1962.       result := reg.ReadString('InfoTip');
  1963.       result := DecodeReferenceString(result);
  1964.  
  1965.       reg.CloseKey;
  1966.     end;
  1967.   finally
  1968.     reg.Free;
  1969.   end;
  1970. end;
  1971.  
  1972. class function TRecycleBinManager.RecyclerGetIntroText: string;
  1973. var
  1974.   reg: TRegistry;
  1975. begin
  1976.   // Not available in some older versions of Windows
  1977.  
  1978.   reg := TRegistry.Create;
  1979.   try
  1980.     reg.RootKey := HKEY_CLASSES_ROOT;
  1981.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  1982.     begin
  1983.       result := reg.ReadString('IntroText');
  1984.       result := DecodeReferenceString(result);
  1985.  
  1986.       reg.CloseKey;
  1987.     end;
  1988.   finally
  1989.     reg.Free;
  1990.   end;
  1991. end;
  1992.  
  1993. class function TRecycleBinManager.RecyclerGetName: string;
  1994. var
  1995.   reg: TRegistry;
  1996. begin
  1997.   // Windows 95b:
  1998.   // Change of CLSID\{645FF040-5081-101B-9F08-00AA002F954E} will change the desktop name of the recycle bin.
  1999.  
  2000.   // Windows 2000: If LocalizedString is available, the 3rd argument will be parsed
  2001.   // (if the third argument will removed, it will be read out from the DLL resource string automatically)
  2002.  
  2003.   reg := TRegistry.Create;
  2004.   try
  2005.     reg.RootKey := HKEY_CLASSES_ROOT;
  2006.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  2007.     begin
  2008.       if reg.ValueExists('LocalizedString') then
  2009.       begin
  2010.         result := reg.ReadString('LocalizedString');
  2011.         result := DecodeReferenceString(result);
  2012.       end
  2013.       else
  2014.       begin
  2015.         result := reg.ReadString('');
  2016.       end;
  2017.  
  2018.       reg.CloseKey;
  2019.     end;
  2020.   finally
  2021.     reg.Free;
  2022.   end;
  2023. end;
  2024.  
  2025. class function TRecycleBinManager.RecyclerEmptyEventGetName: string;
  2026. var
  2027.   reg: TRegistry;
  2028. begin
  2029.   reg := TRegistry.Create;
  2030.   try
  2031.     reg.RootKey := HKEY_CURRENT_USER;
  2032.     if reg.OpenKeyReadOnly('AppEvents\EventLabels\EmptyRecycleBin') then
  2033.     begin
  2034.       result := reg.ReadString('');
  2035.       reg.CloseKey;
  2036.     end;
  2037.   finally
  2038.     reg.Free;
  2039.   end;
  2040. end;
  2041.  
  2042. class function TRecycleBinManager.RecyclerEmptyEventGetCurrentSound: string;
  2043. begin
  2044.   result := RecyclerEmptyEventGetSound('.Current');
  2045. end;
  2046.  
  2047. class function TRecycleBinManager.RecyclerEmptyEventGetDefaultSound: string;
  2048. begin
  2049.   result := RecyclerEmptyEventGetSound('.Default');
  2050. end;
  2051.  
  2052. class procedure TRecycleBinManager.RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
  2053. var
  2054.   reg: TRegistry;
  2055. begin
  2056.   reg := TRegistry.Create;
  2057.   try
  2058.     reg.RootKey := HKEY_CURRENT_USER;
  2059.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2060.     begin
  2061.       reg.GetKeyNames(AStringList);
  2062.       reg.CloseKey;
  2063.     end;
  2064.   finally
  2065.     reg.Free;
  2066.   end;
  2067. end;
  2068.  
  2069. class function TRecycleBinManager.RecyclerEmptyEventGetSound(ACategory: string): string;
  2070. var
  2071.   reg: TRegistry;
  2072. resourcestring
  2073.   LNG_SND_EVENT_CAT_ERROR = 'The category "%s" is not available for the notification event "%s".';
  2074. begin
  2075.   // Outputs an filename or empty string for no sound defined.
  2076.  
  2077.   reg := TRegistry.Create;
  2078.   try
  2079.     reg.RootKey := HKEY_CURRENT_USER;
  2080.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2081.     begin
  2082.       if reg.OpenKeyReadOnly(ACategory) then
  2083.       begin
  2084.         result := reg.ReadString('');
  2085.         reg.CloseKey;
  2086.       end
  2087.       else
  2088.         raise EEventCategoryNotDefined.CreateFmt(LNG_SND_EVENT_CAT_ERROR, [ACategory, 'EmptyRecycleBin']);
  2089.       reg.CloseKey;
  2090.     end;
  2091.   finally
  2092.     reg.Free;
  2093.   end;
  2094. end;
  2095.  
  2096. class function TRecycleBinManager.RecyclerQueryFunctionAvailable: boolean;
  2097. var
  2098.   RBHandle: THandle;
  2099.   SHQueryRecycleBin: TSHQueryRecycleBin;
  2100. begin
  2101.   // Windows 95 without Internet Explorer 4 has no SHQueryRecycleBinA.
  2102.   RBHandle := LoadLibrary(shell32);
  2103.   try
  2104.     if RBHandle <> 0 then
  2105.     begin
  2106.       SHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  2107.       if not Assigned(@SHQueryRecycleBin) then
  2108.       begin
  2109.         FreeLibrary(RBHandle);
  2110.         RBHandle := 0;
  2111.       end;
  2112.     end;
  2113.  
  2114.     result := RBHandle <> 0;
  2115.   finally
  2116.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2117.   end;
  2118. end;
  2119.  
  2120. class function TRecycleBinManager.RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean=false): boolean;
  2121. var
  2122.   Operation: TSHFileOpStruct;
  2123. begin
  2124.   // Template: http://www.dsdt.info/tipps/?id=116
  2125.   with Operation do
  2126.   begin
  2127.     Wnd := hInstance; // OK?
  2128.     wFunc := FO_DELETE;
  2129.     pFrom := PChar(FileOrFolder + #0);
  2130.     pTo := nil;
  2131.     fFlags := FOF_ALLOWUNDO;
  2132.     if not confirmation then fFlags := fFlags or FOF_NOCONFIRMATION;
  2133.   end;
  2134.   Result := SHFileOperation(Operation) = 0;
  2135. end;
  2136.  
  2137. class function TRecycleBinManager.RecyclerGroupPolicyNoRecycleFiles: GPOLICYBOOL;
  2138. var
  2139.   reg: TRegistry;
  2140. begin
  2141.   result := gpUndefined;
  2142.  
  2143.   reg := TRegistry.Create;
  2144.   try
  2145.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2146.     // even if gpedit.msc shows "Not configured"!
  2147.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2148.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2149.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2150.     begin
  2151.       if reg.ValueExists('NoRecycleFiles') then
  2152.       begin
  2153.         if reg.ReadBool('NoRecycleFiles') then
  2154.           result := gpEnabled
  2155.         else
  2156.           result := gpDisabled;
  2157.         Exit;
  2158.       end;
  2159.       reg.CloseKey;
  2160.     end;
  2161.     {$ENDIF}
  2162.  
  2163.     reg.RootKey := HKEY_CURRENT_USER;
  2164.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2165.     begin
  2166.       if reg.ValueExists('NoRecycleFiles') then
  2167.       begin
  2168.         if reg.ReadBool('NoRecycleFiles') then
  2169.           result := gpEnabled
  2170.         else
  2171.           result := gpDisabled;
  2172.       end;
  2173.       reg.CloseKey;
  2174.     end;
  2175.   finally
  2176.     reg.Free;
  2177.   end;
  2178. end;
  2179.  
  2180. class function TRecycleBinManager.RecyclerGroupPolicyConfirmFileDelete: GPOLICYBOOL;
  2181. var
  2182.   reg: TRegistry;
  2183. begin
  2184.   result := gpUndefined;
  2185.   reg := TRegistry.Create;
  2186.   try
  2187.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2188.     // even if gpedit.msc shows "Not configured"!
  2189.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2190.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2191.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2192.     begin
  2193.       if reg.ValueExists('ConfirmFileDelete') then
  2194.       begin
  2195.         if reg.ReadBool('ConfirmFileDelete') then
  2196.           result := gpEnabled
  2197.         else
  2198.           result := gpDisabled;
  2199.         Exit;
  2200.       end;
  2201.       reg.CloseKey;
  2202.     end;
  2203.     {$ENDIF}
  2204.  
  2205.     reg.RootKey := HKEY_CURRENT_USER;
  2206.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2207.     begin
  2208.       if reg.ValueExists('ConfirmFileDelete') then
  2209.       begin
  2210.         if reg.ReadBool('ConfirmFileDelete') then
  2211.           result := gpEnabled
  2212.         else
  2213.           result := gpDisabled;
  2214.       end;
  2215.       reg.CloseKey;
  2216.     end;
  2217.   finally
  2218.     reg.Free;
  2219.   end;
  2220. end;
  2221.  
  2222. class function TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize: integer;
  2223. var
  2224.   reg: TRegistry;
  2225. begin
  2226.   result := -1;
  2227.   reg := TRegistry.Create;
  2228.   try
  2229.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2230.     // even if gpedit.msc shows "Not configured"!
  2231.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2232.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2233.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2234.     begin
  2235.       if reg.ValueExists('RecycleBinSize') then
  2236.       begin
  2237.         result := reg.ReadInteger('RecycleBinSize');
  2238.         Exit;
  2239.       end;
  2240.       reg.CloseKey;
  2241.     end;
  2242.     {$ENDIF}
  2243.  
  2244.     reg.RootKey := HKEY_CURRENT_USER;
  2245.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2246.     begin
  2247.       if reg.ValueExists('RecycleBinSize') then
  2248.       begin
  2249.         result := reg.ReadInteger('RecycleBinSize');
  2250.       end;
  2251.       reg.CloseKey;
  2252.     end;
  2253.   finally
  2254.     reg.Free;
  2255.   end;
  2256. end;
  2257.  
  2258. class function TRecycleBinManager.RecyclerConfirmationDialogEnabled: boolean;
  2259. var
  2260.   gp: GPOLICYBOOL;
  2261. begin
  2262.   gp := RecyclerGroupPolicyConfirmFileDelete;
  2263.   if gp <> gpUndefined then
  2264.   begin
  2265.     result := gp = gpEnabled;
  2266.   end
  2267.   else
  2268.   begin
  2269.     result := RecyclerShellStateConfirmationDialogEnabled;
  2270.   end;
  2271. end;
  2272.  
  2273. class function TRecycleBinManager.RecyclerShellStateConfirmationDialogEnabled: boolean;
  2274. var
  2275.   lpss: SHELLSTATE;
  2276.   bNoConfirmRecycle: boolean;
  2277.  
  2278.   PSHGetSettings: TSHGetSettings;
  2279.   RBHandle: THandle;
  2280.  
  2281.   reg: TRegistry;
  2282.   rbuf: array[0..255] of byte;
  2283. begin
  2284.   PSHGetSettings := nil;
  2285.   result := false; // Avoid warning message
  2286.  
  2287.   RBHandle := LoadLibrary(shell32);
  2288.   try
  2289.     if RBHandle <> 0 then
  2290.     begin
  2291.       PSHGetSettings := GetProcAddress(RBHandle, C_SHGetSettings);
  2292.       if not Assigned(@PSHGetSettings) then
  2293.       begin
  2294.         FreeLibrary(RBHandle);
  2295.         RBHandle := 0;
  2296.       end;
  2297.     end;
  2298.  
  2299.     if (RBHandle <> 0) and Assigned(PSHGetSettings) then
  2300.     begin
  2301.       ZeroMemory(@lpss, SizeOf(lpss));
  2302.       PSHGetSettings(lpss, SSF_NOCONFIRMRECYCLE);
  2303.       bNoConfirmRecycle := (lpss.Flags1 and 4) = 4; // fNoConfirmRecycle
  2304.  
  2305.       result := not bNoConfirmRecycle;
  2306.     end
  2307.     else
  2308.     begin
  2309.       reg := TRegistry.Create;
  2310.       try
  2311.         // API function call failed. Probably because Windows is too old.
  2312.         // Try to read out from registry.
  2313.         // The 3rd bit of the 5th byte of "ShellState" is the value
  2314.         // of "fNoConfirmRecycle".
  2315.  
  2316.         reg.RootKey := HKEY_CURRENT_USER;
  2317.         if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer') then
  2318.         begin
  2319.           ZeroMemory(@rbuf, SizeOf(rbuf));
  2320.           reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf));
  2321.  
  2322.           // Lese 3tes Bit vom 5ten Byte
  2323.           bNoConfirmRecycle := ((rbuf[4] and 4) = 4);
  2324.           result := not bNoConfirmRecycle;
  2325.  
  2326.           reg.CloseKey;
  2327.         end
  2328.         else
  2329.         begin
  2330.           raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSettings])]);
  2331.         end;
  2332.       finally
  2333.         reg.Free;
  2334.       end;
  2335.     end;
  2336.   finally
  2337.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2338.   end;
  2339. end;
  2340.  
  2341. class procedure TRecycleBinManager.RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
  2342. var
  2343.   lpss: SHELLSTATE;
  2344.  
  2345.   PSHGetSetSettings: TSHGetSetSettings;
  2346.   RBHandle: THandle;
  2347.  
  2348.   reg: TRegistry;
  2349.   rbuf: array[0..255] of byte;
  2350.  
  2351.   //dwResult: DWORD;
  2352.   lpdwResult: PDWORD_PTR;
  2353. begin
  2354.   PSHGetSetSettings := nil;
  2355.   lpdwResult := nil;
  2356.  
  2357.   RBHandle := LoadLibrary(shell32);
  2358.   try
  2359.     if RBHandle <> 0 then
  2360.     begin
  2361.       PSHGetSetSettings := GetProcAddress(RBHandle, C_SHGetSetSettings);
  2362.       if not Assigned(@PSHGetSetSettings) then
  2363.       begin
  2364.         FreeLibrary(RBHandle);
  2365.         RBHandle := 0;
  2366.       end;
  2367.     end;
  2368.  
  2369.     if (RBHandle <> 0) and Assigned(PSHGetSetSettings) then
  2370.     begin
  2371.       ZeroMemory(@lpss, SizeOf(lpss));
  2372.  
  2373.       PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, false); // Get
  2374.  
  2375.       // Set 3rd bit equal to NewSetting
  2376.       if NewSetting then
  2377.         lpss.Flags1 := lpss.Flags1 or  $00000004
  2378.       else
  2379.         lpss.Flags1 := lpss.Flags1 and $FFFFFFFB;
  2380.  
  2381.       PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, true); // Set
  2382.  
  2383.       SendMessageTimeout (
  2384.         HWND_BROADCAST, WM_SETTINGCHANGE,
  2385.         0, lParam (pChar ('ShellState')),
  2386.         SMTO_ABORTIFHUNG, 5000, lpdwResult(*dwResult*)
  2387.       );
  2388.     end
  2389.     else
  2390.     begin
  2391.       reg := TRegistry.Create;
  2392.       try
  2393.         // API function call failed. Probably because Windows is too old.
  2394.         // Try to read out from registry.
  2395.         // The 3rd bit of the 5th byte of "ShellState" is the value
  2396.         // of "fNoConfirmRecycle".
  2397.  
  2398.         reg.RootKey := HKEY_CURRENT_USER;
  2399.         if reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer', false) then
  2400.         begin
  2401.           ZeroMemory(@rbuf, SizeOf(rbuf));
  2402.  
  2403.           reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Get
  2404.  
  2405.           // Set 3rd bit equal to NewSetting
  2406.           if NewSetting then
  2407.             rbuf[4] := rbuf[4] or  $04
  2408.           else
  2409.             rbuf[4] := rbuf[4] and $FB;
  2410.  
  2411.           reg.WriteBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Set
  2412.  
  2413.           SendMessageTimeout (
  2414.             HWND_BROADCAST, WM_SETTINGCHANGE,
  2415.             0, lParam (pChar ('ShellState')),
  2416.             SMTO_ABORTIFHUNG, 5000, lpdwResult(*dwResult*)
  2417.           );
  2418.  
  2419.           reg.CloseKey;
  2420.         end
  2421.         else
  2422.         begin
  2423.           raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSetSettings])]);
  2424.         end;
  2425.       finally
  2426.         reg.Free;
  2427.       end;
  2428.     end;
  2429.   finally
  2430.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2431.   end;
  2432. end;
  2433.  
  2434. { TRbRecycleBinItem }
  2435.  
  2436. function TRbRecycleBinItem.GetSource: string;
  2437. begin
  2438.   {$IFDEF UNICODE}
  2439.   result := SourceUnicode;
  2440.   {$ELSE}
  2441.   result := SourceAnsi;
  2442.   {$ENDIF}
  2443. end;
  2444.  
  2445. end.
  2446.