Subversion Repositories recyclebinunit

Rev

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