Subversion Repositories recyclebinunit

Rev

Rev 97 | Rev 100 | 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: 02 JUL 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-07-02';
  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.     vistaId: string;
  820.     wTest: TRbInfoWItem;
  821.     bakPosition: int64;
  822.     testVistaItem: TRbVistaItem;
  823.   begin
  824.     fs := TFileStream.Create(AFile, fmOpenRead);
  825.     try
  826.       fs.Seek(0, soFromBeginning);
  827.  
  828.       {$REGION 'First try if it is a Vista index file'}
  829.       testVistaItem := nil;
  830.       if SameText(copy(ExtractFileName(AFile), 1, 2), '$I') then
  831.       begin
  832.         vistaId := copy(AFile, 3, Length(AFile)-2);
  833.         testVistaItem := TRbVistaItem.Create(fs, AFile, vistaId);
  834.       end
  835.       else
  836.       begin
  837.         vistaId := ''; // manual file that was not named $I..., so we cannot get $R... ID and therefore no physical file!
  838.         try
  839.           testVistaItem := TRbVistaItem.Create(fs, AFile, vistaId);
  840.           if Copy(testVistaItem.Source,2,2) <> ':\' then
  841.             FreeAndNil(testVistaItem);
  842.         except
  843.           testVistaItem := nil;
  844.         end;
  845.       end;
  846.       {$ENDREGION}
  847.  
  848.       if Assigned(testVistaItem) then
  849.       begin
  850.         list.Add(testVistaItem);
  851.       end
  852.       else
  853.       begin
  854.         fs.Seek(0, soFromBeginning);
  855.         if TolerantReading then
  856.         begin
  857.           // This is a special treatment how to recover data from an INFO/INFO2 file
  858.           // which was corrupted by an incompatible multiboot configuration.
  859.           // Example:
  860.           // - Win95 without IE4 and WinNT4 both write into the INFO file. But Win95 appends the ANSI record and WinNT appends an Unicode record.
  861.           // - 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.
  862.           fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  863.           while fs.Position < fs.size do
  864.           begin
  865.             // Can we actually read a Unicode record?
  866.             if fs.Position + SizeOf(TRbInfoRecordW) <= fs.Size then
  867.             begin
  868.               // Try to read the Unicode record and check if it is valid
  869.               // In case it is no Unicode record, then the Unicode part will be the
  870.               // ANSI source name of the next record. In this case, we won't get
  871.               // a ':' at the Unicode string.
  872.               bakPosition := fs.Position;
  873.               wTest := TRbInfoWItem.Create(fs, AFile);
  874.               if Copy(wTest.SourceUnicode, 2, 2) = ':\' then
  875.               begin
  876.                 // Yes, it is a valid Unicode record.
  877.                 list.Add(wTest);
  878.               end
  879.               else
  880.               begin
  881.                 // No, it is not a valid Unicode record. Jump back, and we need
  882.                 // to assume that the following record will be a valid ANSI record.
  883.                 fs.Position := bakPosition;
  884.                 list.Add(TRbInfoAItem.Create(fs, AFile));
  885.               end;
  886.             end
  887.             else if fs.Position + SizeOf(TRbInfoRecordA) <= fs.Size then
  888.             begin
  889.               // No, there is not enough space left for an Unicode record.
  890.               // So we assume that the following record will be a valid ANSI record.
  891.               list.Add(TRbInfoAItem.Create(fs, AFile));
  892.             end
  893.             else
  894.             begin
  895.               // Not enough space to read a Ansi record!
  896.               // Ignore it
  897.             end;
  898.           end;
  899.         end
  900.         else
  901.         begin
  902.           // This is the code for non-tolerant reading of the records.
  903.           fs.ReadBuffer(infoHdr, SizeOf(infoHdr));
  904.           case infoHdr.recordLength of
  905.             SizeOf(TRbInfoRecordA):
  906.             begin
  907.               while fs.Position < fs.size do
  908.               begin
  909.                 list.Add(TRbInfoAItem.Create(fs, AFile));
  910.               end;
  911.             end;
  912.             SizeOf(TRbInfoRecordW):
  913.             begin
  914.               while fs.Position < fs.size do
  915.               begin
  916.                 list.Add(TRbInfoWItem.Create(fs, AFile));
  917.               end;
  918.             end
  919.             else
  920.             begin
  921.               raise Exception.CreateFmt(LNG_INVALID_INFO_FORMAT, [AFile]);
  922.             end;
  923.           end;
  924.         end;
  925.       end;
  926.     finally
  927.       FreeAndNil(fs);
  928.     end;
  929.   end;
  930.  
  931.   procedure _HandleVistaDir(ADirectory: string);
  932.   var
  933.     SR: TSearchRec;
  934.     fs: TFileStream;
  935.     id: string;
  936.   begin
  937.     ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  938.  
  939.     if FindFirst(ADirectory + '$I*', faAnyFile, SR) = 0 then
  940.     begin
  941.       repeat
  942.         id := sr.Name;
  943.         { 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
  944.         id := Copy(id, 3, Length(id)-2);
  945.  
  946.         fs := TFileStream.Create(ADirectory+sr.Name, fmOpenRead);
  947.         try
  948.           fs.Seek(0, soFromBeginning);
  949.           list.Add(TRbVistaItem.Create(fs, ADirectory+sr.Name, id));
  950.         finally
  951.           FreeAndNil(fs);
  952.         end;
  953.       until FindNext(SR) <> 0;
  954.     end;
  955.     FindClose(SR);
  956.   end;
  957.  
  958. begin
  959.   if DirectoryExists(FFileOrDirectory) then // Vista, as well as directories with INFO and INFO2
  960.   begin
  961.     _HandleVistaDir(FFileOrDirectory);
  962.  
  963.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2') then
  964.     begin
  965.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO2');
  966.     end;
  967.  
  968.     if FileExists(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO') then
  969.     begin
  970.       _HandleIndexFile(IncludeTrailingPathDelimiter(FFileOrDirectory) + 'INFO');
  971.     end;
  972.   end
  973.   else if FileExists(FFileOrDirectory) then
  974.   begin
  975.     _HandleIndexFile(FFileOrDirectory); // Either INFO, or INFO2, or a single Vista index file
  976.   end
  977.   else raise Exception.CreateFmt(LNG_FILE_NOT_FOUND, [FFileOrDirectory]);
  978. end;
  979.  
  980. { TRbDrive }
  981.  
  982. procedure TRbDrive.CheckDriveExisting;
  983. begin
  984.   // Does the drive exist?
  985.   // see http://www.delphipraxis.net/post2933.html
  986.   if not GetLogicalDrives and (1 shl DriveNumber) <> 0 then
  987.   begin
  988.     raise EInvalidDrive.CreateFmt(LNG_DRIVE_NOT_EXISTING, [UpperCase(string(FDriveLetter))+':']);
  989.   end;
  990. end;
  991.  
  992. constructor TRbDrive.Create(ADriveLetter: AnsiChar);
  993. begin
  994.   inherited Create;
  995.  
  996.   FDriveLetter := ADriveLetter;
  997.   CheckDriveExisting;
  998. end;
  999.  
  1000. function TRbDrive.DiskSize: integer;
  1001. begin
  1002.   result := SysUtils.DiskSize(DriveNumber+1 {0 is current, 1 is A}) div (1024*1024);
  1003. end;
  1004.  
  1005. function TRbDrive.DriveNumber: integer;
  1006. begin
  1007.   result := DriveLetterToDriveNumber(FDriveLetter);
  1008. end;
  1009.  
  1010. function TRbDrive.GetAPIInfo: TSHQueryRBInfo;
  1011. var
  1012.   PSHQueryRecycleBin: TSHQueryRecycleBin;
  1013.   RBHandle: THandle;
  1014.   res: HRESULT;
  1015.   Path: string;
  1016. begin
  1017.   Path := FDriveLetter + ':\';
  1018.  
  1019.   // Ref: http://www.delphipraxis.net/post1291.html
  1020.  
  1021.   RBHandle := LoadLibrary(shell32);
  1022.   try
  1023.     PSHQueryRecycleBin := nil;
  1024.     if RBHandle <> 0 then
  1025.     begin
  1026.       PSHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  1027.       if not Assigned(@PSHQueryRecycleBin) then
  1028.       begin
  1029.         FreeLibrary(RBHandle);
  1030.         RBHandle := 0;
  1031.       end;
  1032.     end;
  1033.  
  1034.     FillChar(result, SizeOf(TSHQueryRBInfo), 0);
  1035.     result.cbSize := SizeOf(TSHQueryRBInfo);
  1036.  
  1037.     if (RBHandle <> 0) and Assigned(PSHQueryRecycleBin) then
  1038.     begin
  1039.       res := PSHQueryRecycleBin(PChar(Path), result);
  1040.       // if Succeeded(res) then
  1041.       if res = S_OK then
  1042.       begin
  1043.         // Alles OK, unser result hat nun die gewünschten Daten.
  1044.       end
  1045.       else
  1046.       begin
  1047.         // Since Windows Vista, SHQueryRecycleBin will fail with E_FAIL (80004005)
  1048.         // if Path is a floppy or CD drive...
  1049.         raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_ERROR_CODE, [C_SHQueryRecycleBin, Path, '0x'+IntToHex(res, 2*SizeOf(HRESULT))])]);
  1050.       end;
  1051.     end
  1052.     else
  1053.       raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHQueryRecycleBin])]);
  1054.   finally
  1055.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  1056.   end;
  1057. end;
  1058.  
  1059. function TRbDrive.GetMaxPercentUsage: Extended;
  1060. var
  1061.   abs: integer; // in MB
  1062.   rel: integer; // in % (0-100)
  1063.   gpSetting: integer;
  1064. const
  1065.   DEFAULT_PERCENT = 10; // Windows 95 default
  1066. begin
  1067.   gpSetting := TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize;
  1068.   if gpSetting <> -1 then
  1069.     result := gpSetting / 100
  1070.   else if TRecycleBinManager.UsesGlobalSettings then
  1071.     result := TRecycleBinManager.GetGlobalMaxPercentUsage / 100
  1072.   else if OldCapacityPercent(rel) then
  1073.   begin
  1074.     result := rel / 100;
  1075.   end
  1076.   else if NewCapacityAbsolute(abs) then
  1077.   begin
  1078.     result := abs / DiskSize;
  1079.   end
  1080.   else
  1081.   begin
  1082.     result := DEFAULT_PERCENT / 100;
  1083.   end;
  1084. end;
  1085.  
  1086. function TRbDrive.GetMaxAbsoluteUsage: integer;
  1087. var
  1088.   abs: integer; // in MB
  1089.   rel: integer; // in % (0-100)
  1090.   gpSetting: integer;
  1091. const
  1092.   DEFAULT_PERCENT = 10; // Windows 95 default
  1093. begin
  1094.   gpSetting := TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize;
  1095.   if gpSetting <> -1 then
  1096.     result := Ceil(gpSetting/100 * DiskSize)
  1097.   else if TRecycleBinManager.UsesGlobalSettings then
  1098.     result := Ceil(TRecycleBinManager.GetGlobalMaxPercentUsage/100 * DiskSize)
  1099.   else if NewCapacityAbsolute(abs) then
  1100.   begin
  1101.     result := abs;
  1102.   end
  1103.   else if OldCapacityPercent(rel) then
  1104.   begin
  1105.     result := Ceil(rel/100 * DiskSize);
  1106.   end
  1107.   else
  1108.   begin
  1109.     result := Ceil(DEFAULT_PERCENT/100 * DiskSize);
  1110.   end;
  1111. end;
  1112.  
  1113. function TRbDrive.OldCapacityPercent(var res: integer): boolean;
  1114. var
  1115.   reg: TRegistry;
  1116.   purgeInfo: TRbWin95PurgeInfo;
  1117. begin
  1118.   if Win32MajorVersion >= 6 then
  1119.   begin
  1120.     // Only available till Windows XP
  1121.     result := false;
  1122.     exit;
  1123.   end;
  1124.  
  1125.   result := false;
  1126.  
  1127.   reg := TRegistry.Create;
  1128.   try
  1129.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1130.  
  1131.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1132.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1133.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1134.     begin
  1135.       if reg.OpenKeyReadOnly(string(FDriveLetter)) then
  1136.       begin
  1137.         if reg.ValueExists('Percent') then
  1138.         begin
  1139.           // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1140.  
  1141.           res := reg.ReadInteger('Percent');
  1142.           result := true;
  1143.         end;
  1144.       end
  1145.       else
  1146.       begin
  1147.         if reg.ValueExists('PurgeInfo') then
  1148.         begin
  1149.           // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1150.  
  1151.           reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1152.  
  1153.           res := purgeInfo.percentDrive[FDriveLetter];
  1154.           result := true;
  1155.         end;
  1156.       end;
  1157.  
  1158.       reg.CloseKey;
  1159.     end;
  1160.   finally
  1161.     reg.Free;
  1162.   end;
  1163. end;
  1164.  
  1165. function TRbDrive.NewCapacityAbsolute(var res: integer): boolean;
  1166. var
  1167.   reg: TRegistry;
  1168. begin
  1169.   if Win32MajorVersion < 6 then
  1170.   begin
  1171.     // Only available since Windows Vista
  1172.     result := false;
  1173.     exit;
  1174.   end;
  1175.  
  1176.   result := false;
  1177.  
  1178.   reg := TRegistry.Create;
  1179.   try
  1180.     reg.RootKey := HKEY_CURRENT_USER;
  1181.  
  1182.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume') then
  1183.     begin
  1184.       // Windows Vista and upwards
  1185.       if reg.OpenKeyReadOnly(GUIDToString(VolumeGUID)) then
  1186.       begin
  1187.         res := reg.ReadInteger('MaxCapacity'); // in MB
  1188.         result := true;
  1189.       end;
  1190.       reg.CloseKey;
  1191.     end;
  1192.   finally
  1193.     reg.Free;
  1194.   end;
  1195. end;
  1196.  
  1197. function TRbDrive.GetNukeOnDelete: boolean;
  1198. var
  1199.   reg: TRegistry;
  1200.   purgeInfo: TRbWin95PurgeInfo;
  1201. const
  1202.   RES_DEFAULT = false; // Windows 95 default
  1203. begin
  1204.   if TRecycleBinManager.RecyclerGroupPolicyNoRecycleFiles = gpEnabled then
  1205.     result := true
  1206.   else if TRecycleBinManager.UsesGlobalSettings then
  1207.     result := TRecycleBinManager.GetGlobalNukeOnDelete
  1208.   else
  1209.   begin
  1210.     result := RES_DEFAULT;
  1211.  
  1212.     reg := TRegistry.Create;
  1213.     try
  1214.       reg.RootKey := HKEY_CURRENT_USER;
  1215.  
  1216.       if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume') then
  1217.       begin
  1218.         // Windows Vista and upwards
  1219.         if reg.OpenKeyReadOnly(GUIDToString(VolumeGUID)) then
  1220.         begin
  1221.           result := reg.ReadBool('NukeOnDelete');
  1222.         end;
  1223.         reg.CloseKey;
  1224.       end
  1225.       else
  1226.       begin
  1227.         reg.RootKey := HKEY_LOCAL_MACHINE;
  1228.  
  1229.         // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1230.         // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1231.         if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1232.         begin
  1233.           if reg.OpenKeyReadOnly(string(FDriveLetter)) then
  1234.           begin
  1235.             if reg.ValueExists('NukeOnDelete') then
  1236.             begin
  1237.               // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1238.  
  1239.               result := reg.ReadBool('NukeOnDelete');
  1240.             end;
  1241.           end
  1242.           else
  1243.           begin
  1244.             if reg.ValueExists('PurgeInfo') then
  1245.             begin
  1246.               // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1247.  
  1248.               reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1249.  
  1250.               result := ((purgeInfo.NukeOnDeleteBits shr DriveNumber) and 1) = 1;
  1251.             end;
  1252.           end;
  1253.  
  1254.           reg.CloseKey;
  1255.         end;
  1256.       end;
  1257.     finally
  1258.       reg.Free;
  1259.     end;
  1260.   end;
  1261. end;
  1262.  
  1263. function TRbDrive.GetNumItems: int64;
  1264. begin
  1265.   result := GetAPIInfo.i64NumItems;
  1266. end;
  1267.  
  1268. function TRbDrive.GetSize: int64;
  1269. begin
  1270.   result := GetAPIInfo.i64Size;
  1271. end;
  1272.  
  1273. function TRbDrive.GetVolumeGUID: TGUID;
  1274. begin
  1275.   if GetDriveGUID(FDriveLetter, result) <> ERROR_SUCCESS then
  1276.   begin
  1277.     result := NULL_GUID;
  1278.   end;
  1279. end;
  1280.  
  1281. function TRbDrive.GetVolumeGUIDAvailable: boolean;
  1282. begin
  1283.   result := not IsEqualGUID(VolumeGUID, NULL_GUID);
  1284. end;
  1285.  
  1286. function TRbDrive.IsEmpty: boolean;
  1287. begin
  1288.   result := GetNumItems = 0;
  1289. end;
  1290.  
  1291. function TRbDrive.IsFAT: boolean;
  1292. var
  1293.   Dummy2: DWORD;
  1294.   Dummy3: DWORD;
  1295.   FileSystem: array[0..MAX_PATH-1] of char;
  1296.   VolumeName: array[0..MAX_PATH-1] of char;
  1297.   s: string;
  1298. begin
  1299.   s := FDriveLetter + DriveDelim + PathDelim; // ohne die Auslagerung in einen String kommt es zu einer AV in ntdll
  1300.   GetVolumeInformation(PChar(s), VolumeName,
  1301.     SizeOf(VolumeName), nil, Dummy2, Dummy3, FileSystem, SizeOf(FileSystem));
  1302.   result := uppercase(copy(FileSystem, 0, 3)) = 'FAT';
  1303. end;
  1304.  
  1305. procedure TRbDrive.ListRecycleBins(list: TObjectList{TRbRecycleBin}; UserSID: string='');
  1306.  
  1307.   procedure _AddSIDFolders(dir: string; wholeFolder: boolean);
  1308.   var
  1309.     SR: TSearchRec;
  1310.   begin
  1311.     dir := IncludeTrailingPathDelimiter(dir);
  1312.     if FindFirst(dir+'S-*', faAnyFile, SR) = 0 then
  1313.     begin
  1314.       try
  1315.         repeat
  1316.           if (SR.Name = '.') or (SR.Name = '..') or not DirectoryExists(dir + SR.Name) then continue;
  1317.  
  1318.           if wholeFolder then
  1319.           begin
  1320.             // Vista
  1321.             list.Add(TRbRecycleBin.Create(dir+SR.Name, SR.Name));
  1322.           end
  1323.           else
  1324.           begin
  1325.             // Win95 .. WinXP
  1326.             if FileExists(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO2') then
  1327.               list.Add(TRbRecycleBin.Create(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO2', SR.Name));
  1328.             if FileExists(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO') then
  1329.               list.Add(TRbRecycleBin.Create(IncludeTrailingPathDelimiter(dir+SR.Name) + 'INFO', SR.Name));
  1330.           end;
  1331.         until FindNext(SR) <> 0;
  1332.       finally
  1333.         FindClose(SR);
  1334.       end;
  1335.     end;
  1336.   end;
  1337.  
  1338. var
  1339.   dir: string;
  1340. begin
  1341.   // Find recyclers from Windows Vista or higher
  1342.  
  1343.   if IsFAT then
  1344.   begin
  1345.     dir := FDriveLetter + DriveDelim + PathDelim + '$recycle.bin' + PathDelim;
  1346.     if DirectoryExists(dir) then
  1347.     begin
  1348.       list.Add(TRbRecycleBin.Create(dir));
  1349.     end;
  1350.   end
  1351.   else
  1352.   begin
  1353.     if UserSID <> '' then
  1354.     begin
  1355.       dir := FDriveLetter + DriveDelim + PathDelim + '$recycle.bin' + PathDelim + UserSID + PathDelim;
  1356.       if DirectoryExists(dir) then
  1357.       begin
  1358.         list.Add(TRbRecycleBin.Create(dir, UserSID));
  1359.       end;
  1360.     end
  1361.     else
  1362.     begin
  1363.       _AddSIDFolders(FDriveLetter + DriveDelim + PathDelim + '$recycle.bin', true);
  1364.     end;
  1365.   end;
  1366.  
  1367.   // Find recyclers from Windows before Vista
  1368.  
  1369.   if IsFAT then
  1370.   begin
  1371.     dir := FDriveLetter + DriveDelim + PathDelim + 'Recycled' + PathDelim;
  1372.  
  1373.     // Both "recycle bins" are possible if you have multiboot (but do overwrite themselfes if you empty them)
  1374.     if FileExists(dir + 'INFO2') then
  1375.       list.Add(TRbRecycleBin.Create(dir + 'INFO2')); // Windows 95 with Internet Explorer 4 Extension or higher Windows 9x versions
  1376.     if FileExists(dir + 'INFO') then
  1377.       list.Add(TRbRecycleBin.Create(dir + 'INFO')); // Windows 95 native
  1378.   end
  1379.   else
  1380.   begin
  1381.     if UserSID <> '' then
  1382.     begin
  1383.       dir := FDriveLetter + DriveDelim + PathDelim + 'Recycler' + PathDelim + UserSID + PathDelim;
  1384.  
  1385.       if FileExists(dir + 'INFO2') then
  1386.         list.Add(TRbRecycleBin.Create(dir + 'INFO2', UserSID)); // Windows 2000+
  1387.       if FileExists(dir + 'INFO') then
  1388.         list.Add(TRbRecycleBin.Create(dir + 'INFO', UserSID)); // Windows NT 4
  1389.     end
  1390.     else
  1391.     begin
  1392.       _AddSIDFolders(FDriveLetter + DriveDelim + PathDelim + 'Recycler', false);
  1393.     end;
  1394.   end;
  1395. end;
  1396.  
  1397. { TRbInfoAItem }
  1398.  
  1399. procedure TRbInfoAItem.ReadFromStream(stream: TStream);
  1400. var
  1401.   r: TRbInfoRecordA;
  1402.   i: Integer;
  1403. begin
  1404.   stream.ReadBuffer(r, SizeOf(r));
  1405.  
  1406.   FSourceDrive := Chr(Ord('A') + r.sourceDrive);
  1407.  
  1408.   // Win95 with IE4 and Win2000+:
  1409.   // Wenn ein Eintrag aus der INFO/INFO2 gelöscht wird, dann wird das erste Byte
  1410.   // von sourceAnsi auf Null gesetzt, damit die ganze INFO/INFO2 Datei nicht
  1411.   // ständig neu geschrieben werden muss (so wie es bei Win95 und WinNT4 der Fall war).
  1412.   // Wir lesen den Eintrag trotzdem, da unsere Software ja auch zu forensischen
  1413.   // Zwecken eingesetzt werden soll.
  1414.   if r.sourceAnsi[0] = #0 then
  1415.   begin
  1416.     FRemovedEntry := true;
  1417.     r.sourceAnsi[0] := AnsiChar(FSourceDrive);
  1418.   end;
  1419.  
  1420.   FSourceAnsi := r.sourceAnsi;
  1421.  
  1422.   // Unicode does not exist in INFO(1) structure
  1423.   (* FSourceUnicode := AnsiCharArrayToWideString(r.sourceAnsi); *)
  1424.   SetLength(FSourceUnicode, Length(r.sourceAnsi));
  1425.   for i := 0 to Length(r.sourceAnsi)-1 do
  1426.     FSourceUnicode[i+1] := WideChar(r.sourceAnsi[i]);
  1427.  
  1428.   FID := IntToStr(r.recordNumber);
  1429.   FDeletionTime := FileTimeToDateTime(r.deletionTime);
  1430.   FOriginalSize := r.originalSize;
  1431.  
  1432.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1433.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1434.   AnsiRemoveNulChars(FSourceAnsi);
  1435.   UnicodeRemoveNulChars(FSourceUnicode);
  1436. end;
  1437.  
  1438. function TRbInfoAItem.DeleteFile: boolean;
  1439. var
  1440.   r: string;
  1441. begin
  1442.   r := GetPhysicalFile;
  1443.   if DirectoryExists(r) then
  1444.     result := DeleteDirectory(r) // Usually, the old recycle bin does not allow folders. Just to be sure, we include the code.
  1445.   else
  1446.     result := SysUtils.DeleteFile(r); // TODO: geht das oder gibt es zugriffsverletzung? --> Win95: Funktioniert
  1447.  
  1448.   // TODO: nun auch den eintrag aus der INFO-Datei rausschmeißen (Datei neu schreiben)
  1449. end;
  1450.  
  1451. function TRbInfoAItem.GetPhysicalFile: string;
  1452. begin
  1453.   if FRemovedEntry then
  1454.   begin
  1455.     result := '';
  1456.     Exit;
  1457.   end;
  1458.  
  1459.   // e.g. C:\...\DC0.doc
  1460.   result := IncludeTrailingPathDelimiter(ExtractFilePath(IndexFile)) +
  1461.             'D' + (* SourceDrive *) Source[1] + ID + ExtractFileExt(Source);
  1462. end;
  1463.  
  1464. constructor TRbInfoAItem.Create(fs: TStream; AIndexFile: string);
  1465. begin
  1466.   inherited Create;
  1467.   ReadFromStream(fs);
  1468.   FIndexFile := AIndexFile;
  1469. end;
  1470.  
  1471. { TRbInfoWItem }
  1472.  
  1473. procedure TRbInfoWItem.ReadFromStream(stream: TStream);
  1474. var
  1475.   r: TRbInfoRecordW;
  1476. begin
  1477.   stream.ReadBuffer(r, SizeOf(r));
  1478.  
  1479.   // Win95 with IE4 and Win2000+:
  1480.   // Wenn ein Eintrag aus der INFO/INFO2 gelöscht wird, dann wird das erste Byte
  1481.   // von sourceAnsi auf Null gesetzt, damit die ganze INFO/INFO2 Datei nicht
  1482.   // ständig neu geschrieben werden muss (so wie es bei Win95 und WinNT4 der Fall war).
  1483.   // Wir lesen den Eintrag trotzdem, da unsere Software ja auch zu forensischen
  1484.   // Zwecken eingesetzt werden soll.
  1485.   if r.sourceAnsi[0] = #0 then
  1486.   begin
  1487.     FRemovedEntry := true;
  1488.     r.sourceAnsi[0] := AnsiChar(r.sourceUnicode[0]);
  1489.   end;
  1490.  
  1491.   FSourceAnsi := r.sourceAnsi;
  1492.   FSourceUnicode := r.sourceUnicode;
  1493.   FID := IntToStr(r.recordNumber);
  1494.   FSourceDrive := Chr(Ord('A') + r.sourceDrive);
  1495.   FDeletionTime := FileTimeToDateTime(r.deletionTime);
  1496.   FOriginalSize := r.originalSize;
  1497.  
  1498.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1499.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1500.   AnsiRemoveNulChars(FSourceAnsi);
  1501.   UnicodeRemoveNulChars(FSourceUnicode);
  1502. end;
  1503.  
  1504. function TRbInfoWItem.DeleteFile: boolean;
  1505. var
  1506.   r: string;
  1507. begin
  1508.   r := GetPhysicalFile;
  1509.   if DirectoryExists(r) then
  1510.     result := DeleteDirectory(r)
  1511.   else
  1512.     result := SysUtils.DeleteFile(r); // TODO: geht das oder gibt es zugriffsverletzung?
  1513.  
  1514.   // TODO: nun auch den eintrag aus der INFO-Datei rausschmeißen (Erstes Byte auf 0 setzen)
  1515. end;
  1516.  
  1517. function TRbInfoWItem.GetPhysicalFile: string;
  1518. begin
  1519.   if FRemovedEntry then
  1520.   begin
  1521.     result := '';
  1522.     Exit;
  1523.   end;
  1524.  
  1525.   (*
  1526.   This is actually a bit tricky...
  1527.   Win95 will choose the first letter of the AnsiSource name.
  1528.   WinNT will choose the first letter of the UnicodeSource name.
  1529.   WinXP will choose the driveNumber member.
  1530.  
  1531.   Windows XP is kinda buggy when it comes to changing a drive letter.
  1532.   For example, the drive E: was changed to K:
  1533.   The drive letter is 04 (E), the Source name begins with E:\ and the physical file is De0.txt .
  1534.   After the recycle bin is opened the first time:
  1535.   - The recycle bin will show the file origin as K:\ and not as E:\
  1536.   - The file was renamed from De0.txt to Dk0.txt
  1537.   - The file can be recovered at this time
  1538.   When the recycle bin is closed, the INFO2 file will not be corrected (which is a bug).
  1539.   So, if you open the recycle bin again, the record will be marked
  1540.   as deleted in the INFO file (the first byte will be set to 0),
  1541.   because Windows searches for De0.txt and doesn't find it.
  1542.  
  1543.   (This comment also applies to TRbInfoAItem.GetPhysicalFile)
  1544.   *)
  1545.  
  1546.   // e.g. C:\...\DC0.doc
  1547.   result := IncludeTrailingPathDelimiter(ExtractFilePath(IndexFile)) +
  1548.             'D' + SourceDrive (* SourceUnicode[1] *) + ID + ExtractFileExt(SourceUnicode);
  1549. end;
  1550.  
  1551. constructor TRbInfoWItem.Create(fs: TStream; AIndexFile: string);
  1552. begin
  1553.   inherited Create;
  1554.   ReadFromStream(fs);
  1555.   FIndexFile := AIndexFile;
  1556. end;
  1557.  
  1558. { TRbVistaItem }
  1559.  
  1560. procedure TRbVistaItem.ReadFromStream(stream: TStream);
  1561. var
  1562.   r1: TRbVistaRecord1;
  1563.   r2: TRbVistaRecord2Head;
  1564.   r2SourceUnicode: array of WideChar;
  1565.   version: int64;
  1566.   i: Integer;
  1567. resourcestring
  1568.   LNG_VISTA_WRONG_FORMAT = 'Invalid Vista index format version %d';
  1569. begin
  1570.   stream.ReadBuffer(version, SizeOf(version));
  1571.  
  1572.   if version = 1 then
  1573.   begin
  1574.     stream.Seek(0, soBeginning);
  1575.     stream.ReadBuffer(r1, SizeOf(r1));
  1576.  
  1577.     (* FSourceAnsi := AnsiString(WideCharArrayToWideString(r1.sourceUnicode)); *)
  1578.     SetLength(FSourceAnsi, Length(r1.sourceUnicode));
  1579.     for i := 0 to Length(r1.sourceUnicode)-1 do
  1580.       FSourceAnsi[i+1] := AnsiChar(r1.sourceUnicode[i]); // Note: Invalid chars are automatically converted into '?'
  1581.  
  1582.     (* FSourceUnicode := WideCharArrayToWideString(r1.sourceUnicode); *)
  1583.     SetLength(FSourceUnicode, Length(r1.sourceUnicode));
  1584.     for i := 0 to Length(r1.sourceUnicode)-1 do
  1585.       FSourceUnicode[i+1] := r1.sourceUnicode[i];
  1586.  
  1587.     FID := ''; // will be added manually (at the constructor)
  1588.     FSourceDrive := Char(r1.sourceUnicode[1]);
  1589.     FDeletionTime := FileTimeToDateTime(r1.deletionTime);
  1590.     FOriginalSize := r1.originalSize;
  1591.   end
  1592.   else if version = 2 then
  1593.   begin
  1594.     stream.Seek(0, soBeginning);
  1595.     stream.ReadBuffer(r2, SizeOf(r2));
  1596.  
  1597.     SetLength(r2SourceUnicode, SizeOf(WideChar)*(r2.SourceCountChars-1));
  1598.     stream.Read(r2SourceUnicode[0], SizeOf(WideChar)*(r2.sourceCountChars-1));
  1599.  
  1600.     // Invalid chars are automatically converted into '?'
  1601.     (* FSourceAnsi := AnsiString(WideCharArrayToWideString(r2sourceUnicode)); *)
  1602.     SetLength(FSourceAnsi, Length(r2sourceUnicode));
  1603.     for i := 0 to Length(r2sourceUnicode)-1 do
  1604.       FSourceAnsi[i+1] := AnsiChar(r2sourceUnicode[i]);
  1605.  
  1606.     (* FSourceUnicode := WideCharArrayToWideString(r2sourceUnicode); *)
  1607.     SetLength(FSourceUnicode, Length(r2sourceUnicode));
  1608.     for i := 0 to Length(r2sourceUnicode)-1 do
  1609.       FSourceUnicode[i+1] := WideChar(r2sourceUnicode[i]);
  1610.  
  1611.     FID := ''; // will be added manually (at the constructor)
  1612.     FSourceDrive := Char(r2sourceUnicode[1]);
  1613.     FDeletionTime := FileTimeToDateTime(r2.deletionTime);
  1614.     FOriginalSize := r2.originalSize;
  1615.   end
  1616.   else
  1617.   begin
  1618.     raise Exception.CreateFmt(LNG_VISTA_WRONG_FORMAT, [version]);
  1619.   end;
  1620.  
  1621.   // Remove #0 at the end. There are some bugs where #0 is added to ANSI/Unicode read paths?! (probably in the ReadVista stuff)
  1622.   // TODO: Instead of using this workaround, fix "SourceUnicode" and "SourceAnsi" in the first place!
  1623.   AnsiRemoveNulChars(FSourceAnsi);
  1624.   UnicodeRemoveNulChars(FSourceUnicode);
  1625. end;
  1626.  
  1627. function TRbVistaItem.DeleteFile: boolean;
  1628. var
  1629.   r: string;
  1630. begin
  1631.   r := GetPhysicalFile;
  1632.   if DirectoryExists(r) then
  1633.     result := DeleteDirectory(r)
  1634.   else
  1635.     result := SysUtils.DeleteFile(r);
  1636.  
  1637.   SysUtils.DeleteFile(FIndexFile);
  1638. end;
  1639.  
  1640. function TRbVistaItem.GetPhysicalFile: string;
  1641. begin
  1642.   result := FIndexFile;
  1643.   if Pos('$I', Result) = 0 then
  1644.     result := ''
  1645.   else
  1646.     result := StringReplace(Result, '$I', '$R', [rfIgnoreCase]);
  1647. end;
  1648.  
  1649. constructor TRbVistaItem.Create(fs: TStream; AIndexFile, AID: string);
  1650. begin
  1651.   inherited Create;
  1652.   ReadFromStream(fs);
  1653.   FIndexFile := AIndexFile;
  1654.   FID := AID;
  1655. end;
  1656.  
  1657. { TRecycleBinManager }
  1658.  
  1659. class function TRecycleBinManager.EmptyOwnRecyclers(flags: cardinal): boolean;
  1660. var
  1661.   PSHEmptyRecycleBin: TSHEmptyRecycleBin;
  1662.   LibHandle: THandle;
  1663. begin
  1664.   // Source: http://www.dsdt.info/tipps/?id=176
  1665.   result := true;
  1666.   LibHandle := LoadLibrary(shell32);
  1667.   try
  1668.     if LibHandle <> 0 then
  1669.     begin
  1670.       @PSHEmptyRecycleBin := GetProcAddress(LibHandle, C_SHEmptyRecycleBin);
  1671.       if @PSHEmptyRecycleBin <> nil then
  1672.       begin
  1673.         PSHEmptyRecycleBin(hInstance, nil, flags);
  1674.       end
  1675.       else
  1676.         result := false;
  1677.     end
  1678.     else
  1679.       result := false;
  1680.   finally
  1681.     @PSHEmptyRecycleBin := nil;
  1682.     if LibHandle <> 0 then FreeLibrary(LibHandle);
  1683.   end;
  1684. end;
  1685.  
  1686. class function TRecycleBinManager.EmptyOwnRecyclers(sound, progress, confirmation: boolean): boolean;
  1687. const
  1688.   SHERB_NOCONFIRMATION = $00000001;
  1689.   SHERB_NOPROGRESSUI   = $00000002;
  1690.   SHERB_NOSOUND        = $00000004;
  1691. var
  1692.   flags: cardinal;
  1693. begin
  1694.   flags := 0;
  1695.  
  1696.   if not progress then
  1697.     flags := flags or SHERB_NOPROGRESSUI;
  1698.   if not confirmation then
  1699.     flags := flags or SHERB_NOCONFIRMATION;
  1700.   if not sound then
  1701.     flags := flags or SHERB_NOSOUND;
  1702.  
  1703.   result := EmptyOwnRecyclers(flags);
  1704. end;
  1705.  
  1706. class function TRecycleBinManager.GetGlobalMaxPercentUsage: integer;
  1707. var
  1708.   reg: TRegistry;
  1709.   purgeInfo: TRbWin95PurgeInfo;
  1710. const
  1711.   RES_DEFAULT = 10; // Windows 95 - Standardwert
  1712. begin
  1713.   if Win32MajorVersion >= 6 then
  1714.   begin
  1715.     // Only available till Windows XP
  1716.     result := -1;
  1717.     exit;
  1718.   end;
  1719.  
  1720.   result := RES_DEFAULT;
  1721.  
  1722.   reg := TRegistry.Create;
  1723.   try
  1724.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1725.  
  1726.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1727.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1728.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1729.     begin
  1730.       if reg.ValueExists('Percent') then
  1731.       begin
  1732.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1733.  
  1734.         result := reg.ReadInteger('Percent');
  1735.       end
  1736.       else if reg.ValueExists('PurgeInfo') then
  1737.       begin
  1738.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1739.  
  1740.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1741.         result := purgeInfo.percentGlobal;
  1742.       end;
  1743.  
  1744.       reg.CloseKey;
  1745.     end;
  1746.   finally
  1747.     reg.Free;
  1748.   end;
  1749. end;
  1750.  
  1751. class function TRecycleBinManager.GetGlobalNukeOnDelete: boolean;
  1752. var
  1753.   reg: TRegistry;
  1754.   purgeInfo: TRbWin95PurgeInfo;
  1755. const
  1756.   RES_DEFAULT = false; // Windows 95 - Standardwert
  1757. begin
  1758.   if Win32MajorVersion >= 6 then
  1759.   begin
  1760.     // Only available till Windows XP
  1761.     result := false;
  1762.     exit;
  1763.   end;
  1764.  
  1765.   result := RES_DEFAULT;
  1766.  
  1767.   reg := TRegistry.Create;
  1768.   try
  1769.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1770.  
  1771.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1772.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1773.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1774.     begin
  1775.       if reg.ValueExists('NukeOnDelete') then
  1776.       begin
  1777.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1778.  
  1779.         result := reg.ReadBool('NukeOnDelete');
  1780.       end
  1781.       else if reg.ValueExists('PurgeInfo') then
  1782.       begin
  1783.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1784.  
  1785.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1786.         result := (purgeInfo.NukeOnDeleteBits and $8000000) = $8000000; // bit 27
  1787.       end;
  1788.  
  1789.       reg.CloseKey;
  1790.     end;
  1791.   finally
  1792.     reg.Free;
  1793.   end;
  1794. end;
  1795.  
  1796. (* TODO:
  1797. There are more registry values (found in WinXP):
  1798.  
  1799. BitBucket\<driveletter>
  1800.   VolumeSerialNumber
  1801.   IsUnicode
  1802.  
  1803. *)
  1804.  
  1805. class function TRecycleBinManager.UsesGlobalSettings: boolean;
  1806. var
  1807.   reg: TRegistry;
  1808.   purgeInfo: TRbWin95PurgeInfo;
  1809. const
  1810.   RES_DEFAULT = true; // Windows 95 - Standardwert
  1811. begin
  1812.   if Win32MajorVersion >= 6 then
  1813.   begin
  1814.     // Only available till Windows XP
  1815.     result := false;
  1816.     exit;
  1817.   end;
  1818.  
  1819.   result := RES_DEFAULT;
  1820.  
  1821.   reg := TRegistry.Create;
  1822.   try
  1823.     reg.RootKey := HKEY_LOCAL_MACHINE;
  1824.  
  1825.     // Im Auslieferungszustand von Windows 95 ist dieser Schlüssel nicht vorhanden.
  1826.     // Er wird bei der ersten Änderung der Papierkorb-Einstellungen erstellt.
  1827.     if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
  1828.     begin
  1829.       if reg.ValueExists('UseGlobalSettings') then
  1830.       begin
  1831.         // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
  1832.  
  1833.         result := reg.ReadBool('UseGlobalSettings');
  1834.       end
  1835.       else if reg.ValueExists('PurgeInfo') then
  1836.       begin
  1837.         // Windows 95 - Kodierte Informationen liegen in PurgeInfo
  1838.  
  1839.         reg.ReadBinaryData('PurgeInfo', purgeInfo, SizeOf(purgeInfo));
  1840.         result := purgeInfo.bGlobalSettings;
  1841.       end;
  1842.  
  1843.       reg.CloseKey;
  1844.     end;
  1845.   finally
  1846.     reg.Free;
  1847.   end;
  1848. end;
  1849.  
  1850. class procedure TRecycleBinManager.ListDrives(list: TObjectList{TRbDrive});
  1851. var
  1852.   drive: AnsiChar;
  1853. begin
  1854.   for drive := 'A' to 'Z' do
  1855.     if RecycleBinPossible(drive) then
  1856.       list.Add(TRbDrive.Create(drive));
  1857. end;
  1858.  
  1859. class function TRecycleBinManager.OwnRecyclersEmpty: boolean;
  1860. var
  1861.   drives: TObjectList;
  1862.   i: integer;
  1863. begin
  1864.   result := true;
  1865.  
  1866.   drives := TObjectList.Create(true);
  1867.   try
  1868.     ListDrives(drives);
  1869.     for i := 0 to drives.Count - 1 do
  1870.     begin
  1871.       result := result and TRbDrive(drives.Items[i]).IsEmpty;
  1872.       if not result then break;
  1873.     end;
  1874.   finally
  1875.     drives.Free;
  1876.   end;
  1877. end;
  1878.  
  1879. class function TRecycleBinManager.OwnRecyclersNumItems: int64;
  1880. var
  1881.   drives: TObjectList;
  1882.   i: integer;
  1883. begin
  1884.   result := 0;
  1885.  
  1886.   drives := TObjectList.Create(true);
  1887.   try
  1888.     ListDrives(drives);
  1889.     for i := 0 to drives.Count - 1 do
  1890.     begin
  1891.       result := result + TRbDrive(drives.Items[i]).GetNumItems;
  1892.     end;
  1893.   finally
  1894.     drives.Free;
  1895.   end;
  1896. end;
  1897.  
  1898. class function TRecycleBinManager.OwnRecyclersSize: int64;
  1899. var
  1900.   drives: TObjectList;
  1901.   i: integer;
  1902. begin
  1903.   result := 0;
  1904.  
  1905.   drives := TObjectList.Create(true);
  1906.   try
  1907.     ListDrives(drives);
  1908.     for i := 0 to drives.Count - 1 do
  1909.     begin
  1910.       result := result + TRbDrive(drives.Items[i]).GetSize;
  1911.     end;
  1912.   finally
  1913.     drives.Free;
  1914.   end;
  1915. end;
  1916.  
  1917. class function TRecycleBinManager.RecycleBinPossible(Drive: AnsiChar): boolean;
  1918. var
  1919.   typ: Integer;
  1920. begin
  1921.   // Does the drive exist?
  1922.   // see http://www.delphipraxis.net/post2933.html
  1923.   result := GetLogicalDrives and (1 shl DriveLetterToDriveNumber(Drive)) <> 0;
  1924.   if not result then exit;
  1925.  
  1926.   // Is it a fixed drive? (Only they can have recycle bins)
  1927.   // TODO: is that correct, or can also have other drive types have recyclers?
  1928.   typ := GetDriveType(PChar(Drive + ':\'));
  1929.   result := typ = DRIVE_FIXED;
  1930. end;
  1931.  
  1932. class function TRecycleBinManager.RecyclerGetCurrentIconString: string;
  1933. begin
  1934.   if OwnRecyclersEmpty then
  1935.     result := RecyclerGetEmptyIconString
  1936.   else
  1937.     result := RecyclerGetFullIconString;
  1938. end;
  1939.  
  1940. class function TRecycleBinManager.RecyclerGetDefaultIconString: string;
  1941. var
  1942.   reg: TRegistry;
  1943. begin
  1944.   // Please note: The "default" icon is not always the icon of the
  1945.   // current recycle bin in its current state (full, empty)
  1946.   // At Windows 95b, the registry value actually did change every time the
  1947.   // recycle bin state did change, but at Windows 2000 I could not see any
  1948.   // update, even after reboot. So, the registry value is possible fixed as
  1949.   // default = empty on newer OS versions.
  1950.  
  1951.   reg := TRegistry.Create;
  1952.   try
  1953.     reg.RootKey := HKEY_CLASSES_ROOT;
  1954.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1955.     begin
  1956.       result := reg.ReadString('');
  1957.       reg.CloseKey;
  1958.     end;
  1959.   finally
  1960.     reg.Free;
  1961.   end;
  1962. end;
  1963.  
  1964. class function TRecycleBinManager.RecyclerGetEmptyIconString: string;
  1965. var
  1966.   reg: TRegistry;
  1967. begin
  1968.   reg := TRegistry.Create;
  1969.   try
  1970.     reg.RootKey := HKEY_CLASSES_ROOT;
  1971.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1972.     begin
  1973.       result := reg.ReadString('Empty');
  1974.       reg.CloseKey;
  1975.     end;
  1976.   finally
  1977.     reg.Free;
  1978.   end;
  1979. end;
  1980.  
  1981. class function TRecycleBinManager.RecyclerGetFullIconString: string;
  1982. var
  1983.   reg: TRegistry;
  1984. begin
  1985.   reg := TRegistry.Create;
  1986.   try
  1987.     reg.RootKey := HKEY_CLASSES_ROOT;
  1988.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)+'\DefaultIcon') then
  1989.     begin
  1990.       result := reg.ReadString('Full');
  1991.       reg.CloseKey;
  1992.     end;
  1993.   finally
  1994.     reg.Free;
  1995.   end;
  1996. end;
  1997.  
  1998. class function TRecycleBinManager.RecyclerGetInfoTip: string;
  1999. var
  2000.   reg: TRegistry;
  2001. begin
  2002.   // Not available in some older versions of Windows
  2003.  
  2004.   reg := TRegistry.Create;
  2005.   try
  2006.     reg.RootKey := HKEY_CLASSES_ROOT;
  2007.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  2008.     begin
  2009.       result := reg.ReadString('InfoTip');
  2010.       result := DecodeReferenceString(result);
  2011.  
  2012.       reg.CloseKey;
  2013.     end;
  2014.   finally
  2015.     reg.Free;
  2016.   end;
  2017. end;
  2018.  
  2019. class function TRecycleBinManager.RecyclerGetIntroText: string;
  2020. var
  2021.   reg: TRegistry;
  2022. begin
  2023.   // Not available in some older versions of Windows
  2024.  
  2025.   reg := TRegistry.Create;
  2026.   try
  2027.     reg.RootKey := HKEY_CLASSES_ROOT;
  2028.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  2029.     begin
  2030.       result := reg.ReadString('IntroText');
  2031.       result := DecodeReferenceString(result);
  2032.  
  2033.       reg.CloseKey;
  2034.     end;
  2035.   finally
  2036.     reg.Free;
  2037.   end;
  2038. end;
  2039.  
  2040. class function TRecycleBinManager.RecyclerGetName: string;
  2041. var
  2042.   reg: TRegistry;
  2043. begin
  2044.   // Windows 95b:
  2045.   // Change of CLSID\{645FF040-5081-101B-9F08-00AA002F954E} will change the desktop name of the recycle bin.
  2046.  
  2047.   // Windows 2000: If LocalizedString is available, the 3rd argument will be parsed
  2048.   // (if the third argument will removed, it will be read out from the DLL resource string automatically)
  2049.  
  2050.   reg := TRegistry.Create;
  2051.   try
  2052.     reg.RootKey := HKEY_CLASSES_ROOT;
  2053.     if reg.OpenKeyReadOnly('CLSID\'+GUIDToString(RECYCLER_CLSID)) then
  2054.     begin
  2055.       if reg.ValueExists('LocalizedString') then
  2056.       begin
  2057.         result := reg.ReadString('LocalizedString');
  2058.         result := DecodeReferenceString(result);
  2059.       end
  2060.       else
  2061.       begin
  2062.         result := reg.ReadString('');
  2063.       end;
  2064.  
  2065.       reg.CloseKey;
  2066.     end;
  2067.   finally
  2068.     reg.Free;
  2069.   end;
  2070. end;
  2071.  
  2072. class function TRecycleBinManager.RecyclerEmptyEventGetName: string;
  2073. var
  2074.   reg: TRegistry;
  2075. begin
  2076.   reg := TRegistry.Create;
  2077.   try
  2078.     reg.RootKey := HKEY_CURRENT_USER;
  2079.     if reg.OpenKeyReadOnly('AppEvents\EventLabels\EmptyRecycleBin') then
  2080.     begin
  2081.       result := reg.ReadString('');
  2082.       reg.CloseKey;
  2083.     end;
  2084.   finally
  2085.     reg.Free;
  2086.   end;
  2087. end;
  2088.  
  2089. class function TRecycleBinManager.RecyclerEmptyEventGetCurrentSound: string;
  2090. begin
  2091.   result := RecyclerEmptyEventGetSound('.Current');
  2092. end;
  2093.  
  2094. class function TRecycleBinManager.RecyclerEmptyEventGetDefaultSound: string;
  2095. begin
  2096.   result := RecyclerEmptyEventGetSound('.Default');
  2097. end;
  2098.  
  2099. class procedure TRecycleBinManager.RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
  2100. var
  2101.   reg: TRegistry;
  2102. begin
  2103.   reg := TRegistry.Create;
  2104.   try
  2105.     reg.RootKey := HKEY_CURRENT_USER;
  2106.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2107.     begin
  2108.       reg.GetKeyNames(AStringList);
  2109.       reg.CloseKey;
  2110.     end;
  2111.   finally
  2112.     reg.Free;
  2113.   end;
  2114. end;
  2115.  
  2116. class function TRecycleBinManager.RecyclerEmptyEventGetSound(ACategory: string): string;
  2117. var
  2118.   reg: TRegistry;
  2119. resourcestring
  2120.   LNG_SND_EVENT_CAT_ERROR = 'The category "%s" is not available for the notification event "%s".';
  2121. begin
  2122.   // Outputs an filename or empty string for no sound defined.
  2123.  
  2124.   reg := TRegistry.Create;
  2125.   try
  2126.     reg.RootKey := HKEY_CURRENT_USER;
  2127.     if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
  2128.     begin
  2129.       if reg.OpenKeyReadOnly(ACategory) then
  2130.       begin
  2131.         result := reg.ReadString('');
  2132.         reg.CloseKey;
  2133.       end
  2134.       else
  2135.         raise EEventCategoryNotDefined.CreateFmt(LNG_SND_EVENT_CAT_ERROR, [ACategory, 'EmptyRecycleBin']);
  2136.       reg.CloseKey;
  2137.     end;
  2138.   finally
  2139.     reg.Free;
  2140.   end;
  2141. end;
  2142.  
  2143. class function TRecycleBinManager.RecyclerQueryFunctionAvailable: boolean;
  2144. var
  2145.   RBHandle: THandle;
  2146.   SHQueryRecycleBin: TSHQueryRecycleBin;
  2147. begin
  2148.   // Windows 95 without Internet Explorer 4 has no SHQueryRecycleBinA.
  2149.   RBHandle := LoadLibrary(shell32);
  2150.   try
  2151.     if RBHandle <> 0 then
  2152.     begin
  2153.       SHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
  2154.       if not Assigned(@SHQueryRecycleBin) then
  2155.       begin
  2156.         FreeLibrary(RBHandle);
  2157.         RBHandle := 0;
  2158.       end;
  2159.     end;
  2160.  
  2161.     result := RBHandle <> 0;
  2162.   finally
  2163.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2164.   end;
  2165. end;
  2166.  
  2167. class function TRecycleBinManager.RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean=false): boolean;
  2168. var
  2169.   Operation: TSHFileOpStruct;
  2170. begin
  2171.   // Template: http://www.dsdt.info/tipps/?id=116
  2172.   with Operation do
  2173.   begin
  2174.     Wnd := hInstance; // OK?
  2175.     wFunc := FO_DELETE;
  2176.     pFrom := PChar(FileOrFolder + #0);
  2177.     pTo := nil;
  2178.     fFlags := FOF_ALLOWUNDO;
  2179.     if not confirmation then fFlags := fFlags or FOF_NOCONFIRMATION;
  2180.   end;
  2181.   Result := SHFileOperation(Operation) = 0;
  2182. end;
  2183.  
  2184. class function TRecycleBinManager.RecyclerGroupPolicyNoRecycleFiles: GPOLICYBOOL;
  2185. var
  2186.   reg: TRegistry;
  2187. begin
  2188.   result := gpUndefined;
  2189.  
  2190.   reg := TRegistry.Create;
  2191.   try
  2192.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2193.     // even if gpedit.msc shows "Not configured"!
  2194.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2195.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2196.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2197.     begin
  2198.       if reg.ValueExists('NoRecycleFiles') then
  2199.       begin
  2200.         if reg.ReadBool('NoRecycleFiles') then
  2201.           result := gpEnabled
  2202.         else
  2203.           result := gpDisabled;
  2204.         Exit;
  2205.       end;
  2206.       reg.CloseKey;
  2207.     end;
  2208.     {$ENDIF}
  2209.  
  2210.     reg.RootKey := HKEY_CURRENT_USER;
  2211.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2212.     begin
  2213.       if reg.ValueExists('NoRecycleFiles') then
  2214.       begin
  2215.         if reg.ReadBool('NoRecycleFiles') then
  2216.           result := gpEnabled
  2217.         else
  2218.           result := gpDisabled;
  2219.       end;
  2220.       reg.CloseKey;
  2221.     end;
  2222.   finally
  2223.     reg.Free;
  2224.   end;
  2225. end;
  2226.  
  2227. class function TRecycleBinManager.RecyclerGroupPolicyConfirmFileDelete: GPOLICYBOOL;
  2228. var
  2229.   reg: TRegistry;
  2230. begin
  2231.   result := gpUndefined;
  2232.   reg := TRegistry.Create;
  2233.   try
  2234.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2235.     // even if gpedit.msc shows "Not configured"!
  2236.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2237.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2238.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2239.     begin
  2240.       if reg.ValueExists('ConfirmFileDelete') then
  2241.       begin
  2242.         if reg.ReadBool('ConfirmFileDelete') then
  2243.           result := gpEnabled
  2244.         else
  2245.           result := gpDisabled;
  2246.         Exit;
  2247.       end;
  2248.       reg.CloseKey;
  2249.     end;
  2250.     {$ENDIF}
  2251.  
  2252.     reg.RootKey := HKEY_CURRENT_USER;
  2253.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2254.     begin
  2255.       if reg.ValueExists('ConfirmFileDelete') then
  2256.       begin
  2257.         if reg.ReadBool('ConfirmFileDelete') then
  2258.           result := gpEnabled
  2259.         else
  2260.           result := gpDisabled;
  2261.       end;
  2262.       reg.CloseKey;
  2263.     end;
  2264.   finally
  2265.     reg.Free;
  2266.   end;
  2267. end;
  2268.  
  2269. class function TRecycleBinManager.RecyclerGroupPolicyRecycleBinSize: integer;
  2270. var
  2271.   reg: TRegistry;
  2272. begin
  2273.   result := -1;
  2274.   reg := TRegistry.Create;
  2275.   try
  2276.     // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
  2277.     // even if gpedit.msc shows "Not configured"!
  2278.     {$IFDEF GroupPolicyAcceptHKLMTrick}
  2279.     reg.RootKey := HKEY_LOCAL_MACHINE;
  2280.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2281.     begin
  2282.       if reg.ValueExists('RecycleBinSize') then
  2283.       begin
  2284.         result := reg.ReadInteger('RecycleBinSize');
  2285.         Exit;
  2286.       end;
  2287.       reg.CloseKey;
  2288.     end;
  2289.     {$ENDIF}
  2290.  
  2291.     reg.RootKey := HKEY_CURRENT_USER;
  2292.     if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
  2293.     begin
  2294.       if reg.ValueExists('RecycleBinSize') then
  2295.       begin
  2296.         result := reg.ReadInteger('RecycleBinSize');
  2297.       end;
  2298.       reg.CloseKey;
  2299.     end;
  2300.   finally
  2301.     reg.Free;
  2302.   end;
  2303. end;
  2304.  
  2305. class function TRecycleBinManager.RecyclerConfirmationDialogEnabled: boolean;
  2306. var
  2307.   gp: GPOLICYBOOL;
  2308. begin
  2309.   gp := RecyclerGroupPolicyConfirmFileDelete;
  2310.   if gp <> gpUndefined then
  2311.   begin
  2312.     result := gp = gpEnabled;
  2313.   end
  2314.   else
  2315.   begin
  2316.     result := RecyclerShellStateConfirmationDialogEnabled;
  2317.   end;
  2318. end;
  2319.  
  2320. class function TRecycleBinManager.RecyclerShellStateConfirmationDialogEnabled: boolean;
  2321. var
  2322.   lpss: SHELLSTATE;
  2323.   bNoConfirmRecycle: boolean;
  2324.  
  2325.   PSHGetSettings: TSHGetSettings;
  2326.   RBHandle: THandle;
  2327.  
  2328.   reg: TRegistry;
  2329.   rbuf: array[0..255] of byte;
  2330. begin
  2331.   PSHGetSettings := nil;
  2332.   result := false; // Avoid warning message
  2333.  
  2334.   RBHandle := LoadLibrary(shell32);
  2335.   try
  2336.     if RBHandle <> 0 then
  2337.     begin
  2338.       PSHGetSettings := GetProcAddress(RBHandle, C_SHGetSettings);
  2339.       if not Assigned(@PSHGetSettings) then
  2340.       begin
  2341.         FreeLibrary(RBHandle);
  2342.         RBHandle := 0;
  2343.       end;
  2344.     end;
  2345.  
  2346.     if (RBHandle <> 0) and Assigned(PSHGetSettings) then
  2347.     begin
  2348.       ZeroMemory(@lpss, SizeOf(lpss));
  2349.       PSHGetSettings(lpss, SSF_NOCONFIRMRECYCLE);
  2350.       bNoConfirmRecycle := (lpss.Flags1 and 4) = 4; // fNoConfirmRecycle
  2351.  
  2352.       result := not bNoConfirmRecycle;
  2353.     end
  2354.     else
  2355.     begin
  2356.       reg := TRegistry.Create;
  2357.       try
  2358.         // API function call failed. Probably because Windows is too old.
  2359.         // Try to read out from registry.
  2360.         // The 3rd bit of the 5th byte of "ShellState" is the value
  2361.         // of "fNoConfirmRecycle".
  2362.  
  2363.         reg.RootKey := HKEY_CURRENT_USER;
  2364.         if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer') then
  2365.         begin
  2366.           ZeroMemory(@rbuf, SizeOf(rbuf));
  2367.           reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf));
  2368.  
  2369.           // Lese 3tes Bit vom 5ten Byte
  2370.           bNoConfirmRecycle := ((rbuf[4] and 4) = 4);
  2371.           result := not bNoConfirmRecycle;
  2372.  
  2373.           reg.CloseKey;
  2374.         end
  2375.         else
  2376.         begin
  2377.           raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSettings])]);
  2378.         end;
  2379.       finally
  2380.         reg.Free;
  2381.       end;
  2382.     end;
  2383.   finally
  2384.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2385.   end;
  2386. end;
  2387.  
  2388. class procedure TRecycleBinManager.RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
  2389. var
  2390.   lpss: SHELLSTATE;
  2391.  
  2392.   PSHGetSetSettings: TSHGetSetSettings;
  2393.   RBHandle: THandle;
  2394.  
  2395.   reg: TRegistry;
  2396.   rbuf: array[0..255] of byte;
  2397.  
  2398.   //dwResult: DWORD;
  2399.   lpdwResult: PDWORD_PTR;
  2400. begin
  2401.   PSHGetSetSettings := nil;
  2402.   lpdwResult := nil;
  2403.  
  2404.   RBHandle := LoadLibrary(shell32);
  2405.   try
  2406.     if RBHandle <> 0 then
  2407.     begin
  2408.       PSHGetSetSettings := GetProcAddress(RBHandle, C_SHGetSetSettings);
  2409.       if not Assigned(@PSHGetSetSettings) then
  2410.       begin
  2411.         FreeLibrary(RBHandle);
  2412.         RBHandle := 0;
  2413.       end;
  2414.     end;
  2415.  
  2416.     if (RBHandle <> 0) and Assigned(PSHGetSetSettings) then
  2417.     begin
  2418.       ZeroMemory(@lpss, SizeOf(lpss));
  2419.  
  2420.       PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, false); // Get
  2421.  
  2422.       // Set 3rd bit equal to NewSetting
  2423.       if NewSetting then
  2424.         lpss.Flags1 := lpss.Flags1 or  $00000004
  2425.       else
  2426.         lpss.Flags1 := lpss.Flags1 and $FFFFFFFB;
  2427.  
  2428.       PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, true); // Set
  2429.  
  2430.       SendMessageTimeout (
  2431.         HWND_BROADCAST, WM_SETTINGCHANGE,
  2432.         0, lParam (pChar ('ShellState')),
  2433.         SMTO_ABORTIFHUNG, 5000, lpdwResult(*dwResult*)
  2434.       );
  2435.     end
  2436.     else
  2437.     begin
  2438.       reg := TRegistry.Create;
  2439.       try
  2440.         // API function call failed. Probably because Windows is too old.
  2441.         // Try to read out from registry.
  2442.         // The 3rd bit of the 5th byte of "ShellState" is the value
  2443.         // of "fNoConfirmRecycle".
  2444.  
  2445.         reg.RootKey := HKEY_CURRENT_USER;
  2446.         if reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer', false) then
  2447.         begin
  2448.           ZeroMemory(@rbuf, SizeOf(rbuf));
  2449.  
  2450.           reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Get
  2451.  
  2452.           // Set 3rd bit equal to NewSetting
  2453.           if NewSetting then
  2454.             rbuf[4] := rbuf[4] or  $04
  2455.           else
  2456.             rbuf[4] := rbuf[4] and $FB;
  2457.  
  2458.           reg.WriteBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Set
  2459.  
  2460.           SendMessageTimeout (
  2461.             HWND_BROADCAST, WM_SETTINGCHANGE,
  2462.             0, lParam (pChar ('ShellState')),
  2463.             SMTO_ABORTIFHUNG, 5000, lpdwResult(*dwResult*)
  2464.           );
  2465.  
  2466.           reg.CloseKey;
  2467.         end
  2468.         else
  2469.         begin
  2470.           raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSetSettings])]);
  2471.         end;
  2472.       finally
  2473.         reg.Free;
  2474.       end;
  2475.     end;
  2476.   finally
  2477.     if RBHandle <> 0 then FreeLibrary(RBHandle);
  2478.   end;
  2479. end;
  2480.  
  2481. { TRbRecycleBinItem }
  2482.  
  2483. function TRbRecycleBinItem.GetSource: string;
  2484. begin
  2485.   {$IFDEF UNICODE}
  2486.   result := SourceUnicode;
  2487.   {$ELSE}
  2488.   result := SourceAnsi;
  2489.   {$ENDIF}
  2490. end;
  2491.  
  2492. end.
  2493.