Subversion Repositories recyclebinunit

Rev

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