Subversion Repositories recyclebinunit

Rev

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

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