Subversion Repositories recyclebinunit

Rev

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