Subversion Repositories recyclebinunit

Rev

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

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