Subversion Repositories recyclebinunit

Rev

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

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