Subversion Repositories recyclebinunit

Rev

Rev 86 | Rev 89 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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