Subversion Repositories recyclebinunit

Rev

Rev 30 | Rev 73 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
12 daniel-mar 1
////////////////////////////////////////////////////////////////////////////////////
2
// RECYCLE-BIN-FUNCTIONS BY DANIEL MARSCHALL                                      //
3
// E-MAIL: info@daniel-marschall.de                                               //
4
// WEB:    www.daniel-marschall.de                                                //
5
////////////////////////////////////////////////////////////////////////////////////
67 daniel-mar 6
// Revision: 28 SEP 2015                                                          //
12 daniel-mar 7
// This unit is freeware, but please link to my website if you are using it!      //
8
////////////////////////////////////////////////////////////////////////////////////
9
// Successful tested with:                                                        //
28 daniel-mar 10
// Windows 95b (without IE4 Shell Extensions)                                     //
11
// Windows 95b (with IE4 Shell Extensions)                                        //
12
// Windows 98-SE                                                                  //
67 daniel-mar 13
// Windows NT4 SP6                                                                //
28 daniel-mar 14
// Windows XP-SP3                                                                 //
15
// Windows 2000-SP4                                                               //
16
// Windows 2003 Server EE SP1                                                     //
17
// Windows Vista                                                                  //
18
// Windows 7                                                                      //
67 daniel-mar 19
// Windows 10                                                                     //
12 daniel-mar 20
////////////////////////////////////////////////////////////////////////////////////
21
//                                                                                //
22
//  Needs Delphi 4 or higher. If you are using Delphi 4 or 5, you can not use the //
23
//  RecyclerGetDateTime() functions, because the unit "DateUtils" is missing.     //
24
//  Warning! This is a platform unit.                                             //
25
//                                                                                //
26
//  To do! Can you help?                                                          //
67 daniel-mar 27
//    - Win7 : Drive GUIDs                                                        //
28
//    - Win7 : Absolute vs. Relative size limitations                             //
12 daniel-mar 29
//    - WideString-Support (input/output)                                         //
30
//    - Always do EOF before reading anything?                                    //
31
//    - Is it possible to identify a Vista-file that is not named $Ixxxxxx.ext?   //
32
//    - RecyclerGetInfofiles() check additionally for removable device?           //
33
//      RecyclerIsValid() is false.                                               //
25 daniel-mar 34
//    - Make it possible to empty the recycle bin of one specific drive!          //
12 daniel-mar 35
//                                                                                //
36
//  Unknown! Do you know the answer?                                              //
37
//    - How does Windows 9x/NT manage the daylight saving time (if it does)?      //
38
//    - How does Windows Vista react to a RECYCLER\ folder on a NTFS device?      //
39
//    - How does Windows Vista react to a RECYCLED\ folder on a FAT device?       //
40
//                                                                                //
41
//  Thanks to all these who have helped me solving coding problems.               //
42
//  Thanks to SEBA for sending in the Windows Vista trash structure files.        //
43
//  Thanks to OMATA for testing the unit with Delphi 4.                           //
44
//  Thanks to DEITYSOU for making a bugfix of DriveExists()                       //
45
//                                                                                //
46
////////////////////////////////////////////////////////////////////////////////////
47
 
48
(*
49
 
50
== TODO LISTE ==
51
 
67 daniel-mar 52
- Wichtig! Windows XP: InfoTip, IntroText und LocalizedString sind Resourcenangaben und müssen ausgelesen werden!
53
- Testen: Wie reagiert Windows, wenn Bitbucket\C existiert, aber kein Wert 'Percent' hat? Mit der Standardeinstellung?
54
- Bug: Windows 2000 bei bestehenden Windows 95 Partition: Recycler Filename ist dann Recycled und nicht Recycler!
55
- bug? w95 recycled file hat immer selben löschzeitpunkt und größe? war die nicht verschieden?
56
- beachtet? bei leerem papierkorb auf fat ist weder info noch info2 vorhanden?
57
- testen: auch möglich, einen vista papierkorb offline öffnen?
58
- Problem: bei win95(ohne ie4) und win2000 gleichzeitiger installation: es existiert info UND info2!!!
59
- Implement SETTER functions to every kind of configuration thing. (percentage etc)
60
- Registry CURRENT_USER: Funktionen auch für fremde Benutzer zur Verfügung stellen?
61
- Es sollte möglich sein, dass ein Laufwerk mehr als 1 Recycler beinhaltet -- behandeln
12 daniel-mar 62
 
67 daniel-mar 63
=== Future Ideas ===
12 daniel-mar 64
 
67 daniel-mar 65
- Demoapplikation: Dateien statt Text als Explorer-Like (TListView)?
66
- Einzelne Elemente oder alle wiederherstellen oder löschen
67
- Konfiguration für Laufwerke ändern etc
68
- IconString -> TIcon Convertion functions
69
- platzreservierung in mb-angabe berechnen
70
- I don't know if there exists any API function which checks the state at any internal way.
71
- copy/move files from recyclebin
12 daniel-mar 72
 
73
*)
74
 
75
// TODO: Also include BC++ Versions
76
{$IFNDEF BCB}
77
{$DEFINE DEL1UP}
78
{$IFNDEF VER80}
79
{$DEFINE DEL2UP}
80
{$IFNDEF VER90}
81
{$DEFINE DEL3UP}
82
{$IFNDEF VER100}
83
{$DEFINE DEL4UP}
84
{$IFNDEF VER120}
85
{$DEFINE DEL5UP}
86
{$IFNDEF VER130}
87
{$DEFINE DEL6UP}
88
{$IFNDEF VER140}
89
{$DEFINE DEL7UP}
90
{$ENDIF}
91
{$ENDIF}
92
{$ENDIF}
93
{$ENDIF}
94
{$ENDIF}
95
{$ENDIF}
96
{$ENDIF}
97
 
98
{$IFDEF DEL7UP}
99
{$WARN UNSAFE_TYPE OFF}
100
{$WARN UNSAFE_CODE OFF}
101
{$WARN UNSAFE_CAST OFF}
102
{$ENDIF}
103
 
104
{$IFDEF DEL6UP}
105
unit RecyclerFunctions platform;
106
{$ELSE}
107
unit RecyclerFunctions;
108
{$ENDIF}
109
 
110
// Configuration
111
 
112
// If enabled, all functions with parameter "InfofileOrRecycleFolder" will
113
// also accept files which are not the indexfile (then, a INFO2 or INFO file
114
// will be searched in this directory).
115
{.$DEFINE allow_all_filenames}
116
 
117
interface
118
 
119
uses
120
  Windows, SysUtils, Classes, {$IFDEF DEL6UP}DateUtils,{$ENDIF}
121
  ShellApi{$IFNDEF DEL6UP}, FileCtrl{$ENDIF}, Registry,
27 daniel-mar 122
  Messages, BitOps;
12 daniel-mar 123
 
124
type
125
  EUnknownState = class(Exception);
126
  EEventCategoryNotDefined = class(Exception);
127
  EAPICallError = class(Exception);
128
 
129
  PSHQueryRBInfo = ^TSHQueryRBInfo;
130
  TSHQueryRBInfo = packed record
131
    cbSize      : dword;
132
    i64Size     : int64;
133
    i64NumItems : int64;
134
  end;
135
 
17 daniel-mar 136
  GPOLICYBOOL = (gpUndefined, gpEnabled, gpDisabled);
137
 
12 daniel-mar 138
const
139
  RECYCLER_CLSID = '{645FF040-5081-101B-9F08-00AA002F954E}';
140
 
141
{$IFDEF DEL6UP}
142
function RecyclerGetDateTime(drive: char; fileid: string): tdatetime; overload;
143
function RecyclerGetDateTime(drive: char; UserSID: string; fileid: string): tdatetime; overload;
144
function RecyclerGetDateTime(InfofileOrRecycleFolder: string): tdatetime; overload;
145
function RecyclerGetDateTime(InfofileOrRecycleFolder: string; id: string): tdatetime; overload;
146
{$ENDIF}
147
 
148
function RecyclerGetSourceUnicode(drive: char; fileid: string): WideString; overload;
149
function RecyclerGetSourceUnicode(drive: char; UserSID: string; fileid: string): WideString; overload;
150
function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string): WideString; overload;
151
function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string; id: string): WideString; overload;
152
 
153
function RecyclerGetSource(drive: char; fileid: string): string; overload;
154
function RecyclerGetSource(drive: char; UserSID: string; fileid: string): string; overload;
155
function RecyclerGetSource(InfofileOrRecycleFolder: string): string; overload;
156
function RecyclerGetSource(InfofileOrRecycleFolder: string; id: string): string; overload;
157
 
158
procedure RecyclerListIndexes(drive: char; result: TStringList); overload;
159
procedure RecyclerListIndexes(drive: char; UserSID: string; result: TStringList); overload;
160
procedure RecyclerListIndexes(InfofileOrRecycleFolder: string; result: TStringList); overload;
161
 
162
function RecyclerGetSourceDrive(drive: char; fileid: string): char; overload;
163
function RecyclerGetSourceDrive(drive: char; UserSID: string; fileid: string): char; overload;
164
function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string): char; overload;
165
function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string; id: string): char; overload;
166
 
167
function RecyclerOriginalSize(drive: char; fileid: string): integer; overload;
168
function RecyclerOriginalSize(drive: char; UserSID: string; fileid: string): integer; overload;
169
function RecyclerOriginalSize(InfofileOrRecycleFolder: string): integer; overload;
170
function RecyclerOriginalSize(InfofileOrRecycleFolder: string; id: string): integer; overload;
171
 
172
function RecyclerIsValid(drive: char): boolean; overload;
173
function RecyclerIsValid(drive: char; UserSID: string): boolean; overload;
174
function RecyclerIsValid(InfofileOrRecycleFolder: string): boolean; overload;
175
 
176
function RecyclerCurrentFilename(drive: char; fileid: string): string; overload;
177
function RecyclerCurrentFilename(drive: char; UserSID: string; fileid: string): string; overload;
178
function RecyclerCurrentFilename(InfofileOrRecycleFolder: string): string; overload;
179
function RecyclerCurrentFilename(InfofileOrRecycleFolder: string; id: string): string; overload;
180
 
181
function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string): string; overload;
182
function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean): string; overload;
183
function RecyclerGetPath(drive: char; IncludeInfofile: boolean): string; overload;
184
function RecyclerGetPath(drive: char; UserSID: string): string; overload;
185
function RecyclerGetPath(drive: char): string; overload;
186
 
187
procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string; result: TStringList); overload;
188
procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; result: TStringList); overload;
189
procedure RecyclerGetInfofiles(drive: char; IncludeInfofile: boolean; result: TStringList); overload;
190
procedure RecyclerGetInfofiles(drive: char; UserSID: string; result: TStringList); overload;
191
procedure RecyclerGetInfofiles(drive: char; result: TStringList); overload;
192
 
193
function RecyclerCurrentFilenameAndPath(drive: char; UserSID: string; fileid: string): string; overload;
194
function RecyclerCurrentFilenameAndPath(drive: char; fileid: string): string; overload;
195
function RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder: string; id: string): string; overload;
196
 
197
function RecyclerRemoveItem(drive: char; UserSID: string; fileid: string): boolean; overload;
198
function RecyclerRemoveItem(drive: char; fileid: string): boolean; overload;
199
function RecyclerRemoveItem(InfofileOrRecycleFolder: string; id: string): boolean; overload;
200
 
201
procedure RecyclerGetAllRecyclerDrives(result: TStringList);
202
 
203
function RecyclerEmptyRecycleBin(flags: cardinal): boolean; overload;
204
function RecyclerEmptyRecycleBin(sound, progress, confirmation: boolean): boolean; overload;
205
 
206
function RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean): boolean; overload;
207
function RecyclerAddFileOrFolder(FileOrFolder: string): boolean; overload;
208
 
209
function RecyclerConfirmationDialogEnabled: boolean;
17 daniel-mar 210
function RecyclerShellStateConfirmationDialogEnabled: boolean;
12 daniel-mar 211
procedure RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
212
 
213
function RecyclerGetCurrentIconString: string;
214
function RecyclerGetDefaultIconString: string;
215
function RecyclerGetEmptyIconString: string;
216
function RecyclerGetFullIconString: string;
217
 
218
function RecyclerGetName: string;
219
function RecyclerGetInfoTip: string;
220
function RecyclerGetIntroText: string;
221
 
222
function RecyclerEmptyEventGetName: string;
223
function RecyclerEmptyEventGetCurrentSound: string;
224
function RecyclerEmptyEventGetDefaultSound: string;
225
procedure RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
226
function RecyclerEmptyEventGetSound(ACategory: string): string;
227
 
228
function RecyclerGlobalGetPercentUsage: integer;
229
function RecyclerSpecificGetPercentUsage(Drive: Char): integer;
230
function RecyclerGetPercentUsageAutoDeterminate(Drive: Char): integer;
231
 
232
function RecyclerGlobalIsNukeOnDelete: boolean;
233
function RecyclerSpecificIsNukeOnDelete(Drive: Char): boolean;
234
function RecyclerIsNukeOnDeleteAutoDeterminate(Drive: Char): boolean;
235
 
236
function RecyclerHasGlobalSettings: boolean;
237
 
238
function RecyclerIsEmpty: boolean; overload;
239
function RecyclerIsEmpty(Drive: Char): boolean; overload;
240
 
241
function RecyclerGetNumItems: int64; overload;
242
function RecyclerGetNumItems(Drive: Char): int64; overload;
243
 
244
function RecyclerGetSize: int64; overload;
245
function RecyclerGetSize(Drive: Char): int64; overload;
246
 
247
function RecyclerGetAPIInfo(Drive: Char): TSHQueryRBInfo; overload;
248
function RecyclerGetAPIInfo(Path: String): TSHQueryRBInfo; overload;
249
 
250
function RecyclerGetCLSID: string;
251
 
252
// Diese Funktion ist false, wenn sie z.B. unter Windows 95 ohne Internet Explorer
253
// 4.0 Shell Extension ausgeführt wird. Wenn abwärtskompatibler Code geschrieben
254
// werden soll, sollte RecyclerQueryFunctionAvailable() verwendet werden, da
255
// unter Windows 95 folgende Funktionalitäten NICHT vorhanden sind:
256
// - RecyclerIsEmpty
257
// - RecyclerGetNumItems
258
// - RecyclerGetSize
259
// - RecyclerGetAPIInfo
260
function RecyclerQueryFunctionAvailable: boolean;
261
 
17 daniel-mar 262
function RecyclerGroupPolicyNoRecycleFiles: GPOLICYBOOL;
263
function RecyclerGroupPolicyConfirmFileDelete: GPOLICYBOOL;
264
function RecyclerGroupPolicyRecycleBinSize: integer;
265
 
266
function GPBoolToString(value: GPOLICYBOOL): String;
267
 
22 daniel-mar 268
function RecyclerIsPossible(Drive: Char): boolean;
269
 
12 daniel-mar 270
function RecyclerLibraryVersion: string;
271
 
272
implementation
273
 
274
type
275
  SHELLSTATE = record
276
    Flags1: DWORD;
277
(*
278
    BOOL fShowAllObjects : 1;
279
    BOOL fShowExtensions : 1;
280
    BOOL fNoConfirmRecycle : 1;
281
 
282
    BOOL fShowSysFiles : 1;
283
    BOOL fShowCompColor : 1;
284
    BOOL fDoubleClickInWebView : 1;
285
    BOOL fDesktopHTML : 1;
286
    BOOL fWin95Classic : 1;
287
    BOOL fDontPrettyPath : 1;
288
    BOOL fShowAttribCol : 1; // No longer used, dead bit
289
    BOOL fMapNetDrvBtn : 1;
290
    BOOL fShowInfoTip : 1;
291
    BOOL fHideIcons : 1;
292
    BOOL fWebView : 1;
293
    BOOL fFilter : 1;
294
    BOOL fShowSuperHidden : 1;
295
    BOOL fNoNetCrawling : 1;
296
*)
297
    dwWin95Unused: DWORD; // Win95 only - no longer supported pszHiddenFileExts
298
    uWin95Unused: UINT; // Win95 only - no longer supported cbHiddenFileExts
299
 
300
    // Note: Not a typo!  This is a persisted structure so we cannot use LPARAM
301
    lParamSort: Integer;
302
    iSortDirection: Integer;
303
 
304
    version: UINT;
305
 
306
    // new for win2k. need notUsed var to calc the right size of ie4 struct
307
    // FIELD_OFFSET does not work on bit fields
308
    uNotUsed: UINT; // feel free to rename and use
309
    Flags2: DWORD;
310
(*
311
    BOOL fSepProcess: 1;
312
    // new for Whistler.
313
    BOOL fStartPanelOn: 1;       //Indicates if the Whistler StartPanel mode is ON or OFF.
314
    BOOL fShowStartPage: 1;      //Indicates if the Whistler StartPage on desktop is ON or OFF.
315
    UINT fSpareFlags : 13;
316
*)
317
  end;
318
  LPSHELLSTATE = ^SHELLSTATE;
319
 
320
const
321
  {$IFDEF MSWINDOWS}
322
    shell32  = 'shell32.dll';
323
    advapi32 = 'advapi32.dll';
324
  {$ENDIF}
325
  {$IFDEF LINUX}
326
    shell32  = 'libshell32.borland.so';
327
    advapi32 = 'libwine.borland.so';
328
  {$ENDIF}
329
 
330
  // Masks for the shellstate
331
   SSF_SHOWALLOBJECTS  = $00000001;
332
  SSF_SHOWEXTENSIONS  = $00000002;
333
  SSF_HIDDENFILEEXTS  = $00000004;
334
  SSF_SERVERADMINUI   = $00000004;
335
  SSF_SHOWCOMPCOLOR   = $00000008;
336
  SSF_SORTCOLUMNS     = $00000010;
337
  SSF_SHOWSYSFILES    = $00000020;
338
  SSF_DOUBLECLICKINWEBVIEW = $00000080;
339
  SSF_SHOWATTRIBCOL   = $00000100;
340
  SSF_DESKTOPHTML     = $00000200;
341
  SSF_WIN95CLASSIC    = $00000400;
342
  SSF_DONTPRETTYPATH  = $00000800;
343
  SSF_SHOWINFOTIP     = $00002000;
344
  SSF_MAPNETDRVBUTTON = $00001000;
345
  SSF_NOCONFIRMRECYCLE = $00008000;
346
  SSF_HIDEICONS       = $00004000;
347
  SSF_FILTER          = $00010000;
348
  SSF_WEBVIEW         = $00020000;
349
  SSF_SHOWSUPERHIDDEN = $00040000;
350
  SSF_SEPPROCESS      = $00080000;
351
  SSF_NONETCRAWLING   = $00100000;
352
  SSF_STARTPANELON    = $00200000;
353
  SSF_SHOWSTARTPAGE   = $00400000;
354
 
355
// **********************************************************
356
// COMPATIBILITY FUNCTIONS
357
// **********************************************************
358
 
359
{$IFNDEF DEL5UP}
360
function IncludeTrailingBackslash(str: string): string;
361
begin
362
  if Copy(str, length(str), 1) = '\' then    // TODO? Gibt es PathDelim in Delphi 4?
363
    Result := str
364
  else
365
    Result := str + '\';
366
end;
367
{$ENDIF}
368
 
369
// **********************************************************
370
// INTERNALLY USED FUNCTIONS
371
// **********************************************************
372
 
373
resourcestring
374
  LNG_UNEXPECTED_STATE = 'Cannot determinate state of "%s" because of an unknown value in the configuration of your operation system. Please contact the developer of the Recycler Bin Unit and help improving the determination methods!';
375
  LNG_API_CALL_ERROR = 'Error while calling the API. Additional information: "%s".';
376
  LNG_NOT_CALLABLE = '%s not callable';
24 daniel-mar 377
  LNG_ERROR_CODE = '%s (Arguments: %s) returns error code %s';
12 daniel-mar 378
 
30 daniel-mar 379
function _FileSize(FileName: string): int64;
12 daniel-mar 380
var
25 daniel-mar 381
  fs: TFileStream;
12 daniel-mar 382
begin
25 daniel-mar 383
  fs := TFileStream.Create(FileName, fmOpenRead);
384
  try
385
    result := fs.size;
386
  finally
387
    fs.free;
388
  end;
12 daniel-mar 389
end;
390
 
391
function _DriveNum(Drive: Char): Byte;
392
// a->0, ..., z->25
393
var
394
  tmp: string;
395
begin
396
  tmp := LowerCase(Drive);
397
  result := Ord(tmp[1])-Ord('a');
398
end;
399
 
400
function _registryReadDump(AReg: TRegistry; AName: string): string;
401
const
402
  // Win2000 RegEdit has set the max input length of a REG_BINARY to $3FFF.
403
  // Probably its the longest possible binary string and not just a GUI limit.
404
  BufMax = $3FFF;
405
var
406
  buf: array[0..BufMax] of byte;
407
  i: integer;
408
  realsize: integer;
409
begin
410
  realsize := AReg.ReadBinaryData(AName, buf, SizeOf(buf));
411
 
412
  for i := 0 to realsize-1 do
413
  begin
414
    result := result + chr(buf[i]);
415
  end;
416
end;
417
 
418
function _GetStringFromDLL(filename: string; num: integer): string;
419
const
420
  // http://www.eggheadcafe.com/forumarchives/vcmfc/sep2005/post23917443.asp
421
  MAX_BUF = 4097; // OK?
422
var
423
  hLib: THandle;
424
  buf: array[0..MAX_BUF] of char;
425
begin
426
  hLib := LoadLibrary(PChar(filename));
427
  try
428
    LoadString(hLib, num, buf, sizeof(buf));
429
    result := buf;
430
  finally
431
    FreeLibrary(hLib);
432
  end;
433
end;
434
 
435
// http://www.delphi-library.de/topic_Umgebungsvariable+in+einem+String+aufloesen_20516,0.html
436
function _ExpandEnvStr(const szInput: string): string;
437
const
438
  MAXSIZE = 32768; // laut PSDK sind 32k das Maximum
439
begin
440
  SetLength(Result,MAXSIZE);
441
  SetLength(Result,ExpandEnvironmentStrings(pchar(szInput),
442
    @Result[1],length(Result))-1); //-1 um abschließendes #0 zu verwerfen
443
end;
444
 
445
// Beispiele
446
// Papierkorb                                                 -- Windows 95
447
// @C:\WINNT\system32\shell32.dll,-8964@1031,Papierkorb       -- Windows 2000
448
 
449
function _DecodeReferenceString(s: string): string;
450
var
451
  dll, id, lang, cache: string;
452
  sl, sl2: tstringlist;
453
begin
454
  if Copy(s, 1, 1) = '@' then
455
  begin
456
    // Referenz auf eine DLL
457
    // @<dll>,-<id>[@<lang>][,<cache>]
458
 
459
    sl := TStringList.Create;
460
    try
461
      // '@' am Anfang entfernen
462
      s := Copy(s, 2, length(s)-1);
463
 
464
      // Nach ',' auftrennen
465
      // sl[0] --> dll
466
      // sl[1] --> -id@lang
467
      // sl[2] --> cache
468
      sl.CommaText := s;
469
 
470
      if sl.Count > 2 then
471
      begin
472
        // Das Ergebnis ist bereits im Klartext vorhanden und muss nicht extrahiert werden
473
        // Ist bei Windows 2000 der Fall
474
        cache := sl[2];
475
        result := cache;
476
        exit;
477
      end;
478
 
479
      if sl.Count > 1 then
480
      begin
481
        dll := sl[0];
482
 
483
        sl2 := TStringList.Create;
484
        try
485
          // Nach '@' auftrennen
486
          // sl2[0] --> id
487
          // sl2[1] --> lang
488
          sl2.CommaText := StringReplace(sl[1], '@', ',', [rfReplaceAll]);
489
 
490
          id := sl2[0];
491
 
492
          if sl2.Count > 1 then
493
          begin
494
            // ToDo: In Zukunft beachten, sofern möglich
495
            lang := sl2[1];
496
          end;
497
 
498
          // Umgebungsvariablen erkennen und Minuszeichen entfernen
499
          result := _GetStringFromDLL(_ExpandEnvStr(dll), -StrToInt(id));
500
        finally
501
          sl2.Free;
502
        end;
503
      end
504
      else
505
      begin
506
        // Zu wenige Informationen!
507
 
508
        result := '';
509
      end;
510
    finally
511
      sl.Free;
512
    end;
513
  end
514
  else
515
  begin
516
    // Kein Hinweis auf eine Referenz
517
    result := s;
518
  end;
519
end;
520
 
521
function _readInt8(const Stream: TStream): byte;
522
var
523
  I: integer;
524
begin
525
  i := 0;
526
  Stream.ReadBuffer(i, 1);
527
  Result := i;
528
end;
529
 
530
function _readInt32(const Stream: TStream): Longword;
531
var
532
  I: integer;
533
begin
534
  i := 0;
535
  Stream.ReadBuffer(i, 4);
536
  Result := i;
537
end;
538
 
539
function _readInt64(const Stream: TStream): int64;
540
var
541
  I: int64;
542
begin
543
  i := 0;
544
  Stream.ReadBuffer(i, 8);
545
  Result := i;
546
end;
547
 
548
function _readChar(const Stream: TStream): char;
549
var
550
  C: Char;
551
begin
552
  C := #0;
553
  Stream.ReadBuffer(C, 1);
554
  Result := C;
555
end;
556
 
557
function _readNullTerminatedString(const Stream: TStream): String;
558
var
559
  S: String;
560
  C: Char;
561
begin
562
  S := '';
563
  repeat
564
    Stream.ReadBuffer(C, 1);
565
    if (C <> #0) then
566
      S := S + C;
567
  until C = #0;
568
  Result := S;
569
end;
570
 
571
// http://www.delphipraxis.net/post761928.html#761928
572
function _readNullTerminatedWideString(const Stream: TStream): WideString;
573
var
574
  S: WideString;
575
  WC: WideChar;
576
begin
577
  S := '';
578
  repeat
579
    Stream.ReadBuffer(WC, 2);
580
    if (WC <> #0) then
581
      S := S + WC;
582
  until WC = #0;
583
  Result := S;
584
end;
585
 
586
// http://www.delphipraxis.net/post340194.html#340194
587
function _nowUTC: TDateTime;
588
var
589
  SystemTime: TSystemTime;
590
begin
591
  GetSystemTime(SystemTime);
592
  with SystemTime do
593
  begin
594
    Result := EncodeDate(wYear, wMonth, wDay) +
595
              EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
596
  end;
597
end;
598
 
599
{$IFDEF DEL6UP}
600
function _getGMTDifference(): extended;
601
begin
602
  result := - (datetimetounix(_nowUTC())-datetimetounix(Now())) / 3600;
603
end;
604
 
605
function _fileTimeToDateTime(FileTime: int64): TDateTime;
606
begin
607
  // http://www.e-fense.com/helix/Docs/Recycler_Bin_Record_Reconstruction.pdf
608
  // UnixTime = 0.0000001 * NTTime + 11644473600
609
  // This is wrong! The correct formula is:
610
  // UnixTime = 0.0000001 * NTTime - 11644473600 + c * 3600
611
  // c = GMT-Difference (MEZ = 1) inclusive daylight saving time (+3600 seconds)
612
  result := unixtodatetime(FileTime div 10000000 - 11644473600 + round(_getGMTDifference() * 3600));
613
end;
614
{$ENDIF}
615
 
616
// http://www.delphipraxis.net/post471470.html
617
function _getAccountSid(const Server, User: WideString; var Sid: PSID): DWORD;
618
var
619
  dwDomainSize, dwSidSize: DWord;
620
  R: LongBool;
621
  wDomain: WideString;
622
  Use: DWord;
623
begin
624
  Result := 0;
625
  SetLastError(0);
626
  dwSidSize := 0;
627
  dwDomainSize := 0;
628
  R := LookupAccountNameW(PWideChar(Server), PWideChar(User), nil, dwSidSize,
629
       nil, dwDomainSize, Use);
630
  if (not R) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
631
  begin
632
    SetLength(wDomain, dwDomainSize);
633
    Sid := GetMemory(dwSidSize);
634
    R := LookupAccountNameW(PWideChar(Server), PWideChar(User), Sid,
635
         dwSidSize, PWideChar(wDomain), dwDomainSize, Use);
636
    if not R then
637
    begin
638
      FreeMemory(Sid);
639
      Sid := nil;
640
    end;
641
  end
642
  else
643
    Result := GetLastError;
644
end;
645
 
67 daniel-mar 646
const
647
  UNLEN = 256; // lmcons.h
648
 
12 daniel-mar 649
// Template:
650
// http://www.latiumsoftware.com/en/pascal/0014.php
651
function _getLoginNameW: widestring;
652
var
67 daniel-mar 653
  Buffer: array[0..UNLEN] of widechar;
654
  Size: DWORD;
12 daniel-mar 655
begin
67 daniel-mar 656
  Size := SizeOf(Buffer);
12 daniel-mar 657
  if GetUserNameW(Buffer, Size) then
658
    Result := Buffer
659
  else
660
    Result := 'User';
661
end;
662
 
67 daniel-mar 663
function _ConvertSidToStringSidA(SID: PSID; var strSID: LPSTR): boolean;
12 daniel-mar 664
type
665
  DllReg = function(SID: PSID; var StringSid: LPSTR): Boolean; stdcall;
666
var
667
  hDll: THandle;
668
  dr: DllReg;
669
begin
670
  result := false;
671
  hDll := LoadLibrary(advapi32);
672
  if hDll <> 0 then
673
  begin
674
    @dr := GetProcAddress(hDll, 'ConvertSidToStringSidA');
67 daniel-mar 675
 
12 daniel-mar 676
    if assigned(dr) then
677
    begin
678
      result := dr(SID, strSID);
679
    end;
680
  end;
681
end;
682
 
67 daniel-mar 683
const
684
  winternl_lib = 'Ntdll.dll';
685
 
686
type
687
  USHORT = Word;
688
  PWSTR = PWidechar;
689
  PCWSTR = PWideChar;
690
 
691
   NTSTATUS = Longword;
692
 
693
  _UNICODE_STRING = record
694
    Length: USHORT;
695
    MaximumLength: USHORT;
696
    Buffer: PWSTR;
697
  end;
698
  UNICODE_STRING = _UNICODE_STRING;
699
  PUNICODE_STRING = ^UNICODE_STRING;
700
 
701
function _RtlConvertSidToUnicodeString(
702
  UnicodeString: PUNICODE_STRING;
703
  Sid: PSID;
704
  AllocateDestinationString: BOOLEAN): NTSTATUS; stdcall;
705
type
706
  DllReg = function(UnicodeString: PUNICODE_STRING;
707
  Sid: PSID;
708
  AllocateDestinationString: BOOLEAN): NTSTATUS; stdcall;
709
var
710
  hDll: THandle;
711
  dr: DllReg;
712
begin
713
  result := $FFFFFFFF;
714
  hDll := LoadLibrary(winternl_lib);
715
  if hDll = 0 then Exit;
716
  try
717
    @dr := GetProcAddress(hDll, 'RtlConvertSidToUnicodeString');
718
    if not Assigned(dr) then Exit;
719
    result := dr(UnicodeString, Sid, AllocateDestinationString);
720
  finally
721
    FreeLibrary(hDll);
722
  end;
723
end;
724
 
725
procedure _RtlFreeUnicodeString(UnicodeString: PUNICODE_STRING); stdcall;
726
type
727
  DllReg = procedure(UnicodeString: PUNICODE_STRING); stdcall;
728
var
729
  hDll: THandle;
730
  dr: DllReg;
731
begin
732
  hDll := LoadLibrary(winternl_lib);
733
  if hDll = 0 then Exit;
734
  try
735
    @dr := GetProcAddress(hDll, 'RtlFreeUnicodeString');
736
    if not Assigned(dr) then Exit;
737
    dr(UnicodeString);
738
  finally
739
    FreeLibrary(hDll);
740
  end;
741
end;
742
 
743
function _NT_SidToString(SID: PSID; var strSID: string): boolean;
744
var
745
  pus: PUNICODE_STRING;
746
  us: UNICODE_STRING;
747
begin
748
  pus := @us;
749
  result := _RtlConvertSidToUnicodeString(pus, SID, true) = 0;
750
  if not result then Exit;
751
  strSID := pus^.Buffer;
752
  UniqueString(strSID);
753
  _RtlFreeUnicodeString(pus);
754
  result := true;
755
end;
756
 
12 daniel-mar 757
// http://www.delphipraxis.net/post471470.html
758
// Changed
759
function _getMySID(): string;
760
var
761
  SID: PSID;
762
  strSID: PAnsiChar;
763
  err: DWORD;
764
begin
765
  SID := nil;
766
 
767
  err := _getAccountSid('', _getLoginNameW(), SID);
67 daniel-mar 768
  if err > 0 then
769
  begin
770
    EAPICallError.Create('_getAccountSid:' + SysErrorMessage(err));
771
    Exit;
772
  end;
12 daniel-mar 773
 
67 daniel-mar 774
  if _ConvertSidToStringSidA(SID, strSID) then
12 daniel-mar 775
  begin
67 daniel-mar 776
    result := string(strSID);
777
    Exit;
778
  end;
12 daniel-mar 779
 
67 daniel-mar 780
  if _NT_SidToString(SID, result) then Exit;
781
 
782
  EAPICallError.Create('_getMySID:' + SysErrorMessage(err));
12 daniel-mar 783
end;
784
 
785
// Originalcode aus http://www.delphipraxis.net/post2933.html
786
function _DriveExists(DriveByte: Byte): Boolean; overload;
787
begin
788
  Result := GetLogicalDrives and (1 shl DriveByte) <> 0;
789
end;
790
 
791
function _driveExists(Drive: Char): Boolean; overload;
792
var
793
  DriveByte: Byte;
794
  tmp: string;
795
begin
796
  // Make drive letter upper case (for older Delphi versions)
797
  tmp := UpperCase(Drive);
798
  Drive := tmp[1];
799
 
800
  DriveByte := Ord(Drive) - Ord('A');
801
  Result := _DriveExists(DriveByte);
802
end;
803
 
804
function _isFAT(drive: char): boolean;
805
var
806
  Dummy2: DWORD;
807
  Dummy3: DWORD;
808
  FileSystem: array[0..MAX_PATH] of char;
809
  VolumeName: array[0..MAX_PATH] of char;
810
  s: string;
811
begin
812
  result := false;
813
  if _driveExists(drive) then
814
  begin
815
    s := drive + DriveDelim + PathDelim; // ohne die Auslagerung in einen String kommt es zu einer AV in ntdll
816
    GetVolumeInformation(PChar(s), VolumeName,
817
      SizeOf(VolumeName), nil, Dummy2, Dummy3, FileSystem, SizeOf(FileSystem));
818
    result := uppercase(copy(FileSystem, 0, 3)) = 'FAT';
819
  end;
820
end;
821
 
822
// **********************************************************
25 daniel-mar 823
// VISTA AND WINDOWS 7 FUNCTIONS, INTERNAL USED
12 daniel-mar 824
// **********************************************************
825
 
25 daniel-mar 826
const
827
  vista_valid_index_size = $220; // 544
828
 
12 daniel-mar 829
function _isFileVistaRealfile(filename: string): boolean;
830
begin
831
  result := uppercase(copy(extractfilename(filename), 0, 2)) = '$R';
832
end;
833
 
834
function _isFileVistaIndexfile(filename: string): boolean;
835
begin
836
  result := uppercase(copy(extractfilename(filename), 0, 2)) = '$I';
837
end;
838
 
839
function _isFileVistaNamed(filename: string): boolean;
840
begin
841
  result := _isFileVistaIndexfile(filename) or
842
            _isFileVistaRealfile(filename);
843
end;
844
 
845
function _VistaChangeRealfileToIndexfile(realfile: string): string;
846
begin
847
  if _isFileVistaRealfile(realfile) then
848
  begin
849
    result := extractfilepath(realfile)+'$I'+
850
      copy(extractfilename(realfile), 3, length(extractfilename(realfile))-2);
851
  end
852
  else
853
    result := realfile; // ignore, even if it is not a vista recycle-file
854
end;
855
 
856
function _VistaChangeIndexfileToRealfile(indexfile: string): string;
857
begin
858
  if _isFileVistaIndexfile(indexfile) then
859
  begin
860
    result := extractfilepath(indexfile)+'$R'+
861
      copy(extractfilename(indexfile), 3, length(extractfilename(indexfile))-2);
862
  end
863
  else
864
    result := indexfile; // ignore, even if it is not a vista recycle-file
865
end;
866
 
867
procedure _VistaListIndexes(recyclerpath: string; result: TStringList);
868
var
869
  sr: TSearchRec;
870
  r: Integer;
871
  tmp: string;
872
begin
873
  tmp := recyclerpath;
874
  tmp := IncludeTrailingBackslash(tmp);
875
 
876
  if not directoryexists(tmp) then exit;
877
 
878
  r := FindFirst(tmp+PathDelim + '$I*', faAnyFile, sr);
879
  while r = 0 do
880
  begin
881
    if (sr.Name <> '.') and (sr.Name <> '..') then
882
    begin
25 daniel-mar 883
      if sr.Size = vista_valid_index_size then
12 daniel-mar 884
      begin
885
        result.Add(copy(sr.name, 3, length(sr.name)-2));
886
      end;
887
    end;
888
    r := FindNext(sr);
889
  end;
890
 
891
  FindClose(sr);
892
end;
893
 
894
function _VistaCurrentFilename(infofilename: string): string;
895
begin
896
  result := extractfilename(infofilename);
897
 
898
  if _isFileVistaRealfile(result) then
899
  begin
900
    exit;
901
  end;
902
 
903
  if _isFileVistaIndexfile(result) then
904
  begin
905
    result := _VistaChangeIndexfileToRealfile(result);
906
    exit;
907
  end;
908
 
909
  result := copy(result, 3, length(result)-2);
910
  result := '$R'+result;
911
end;
912
 
913
function _VistaGetSourceDrive(infofile: string): char;
914
var
915
  fs: TFileStream;
916
  tmp: string;
917
const
918
  drive_vista_position = $18;
919
begin
920
  result := #0;
921
 
922
  tmp := infofile;
923
  tmp := _VistaChangeRealfileToIndexfile(tmp);
924
  if not fileexists(tmp) then exit;
925
 
926
  fs := TFileStream.Create(tmp, fmOpenRead);
927
  try
928
    fs.seek(drive_vista_position, soFromBeginning);
929
    result := _readChar(fs);
930
  finally
931
    fs.free;
932
  end;
933
end;
934
 
935
{$IFDEF DEL6UP}
936
function _VistaGetDateTime(infofile: string): TDateTime;
937
var
938
  fs: TFileStream;
939
  tmp: string;
940
const
941
  timestamp_vista_position = $10;
942
begin
943
  result := EncodeDateTime(1601, 1, 1, 0, 0, 0, 0);
944
 
945
  tmp := infofile;
946
  tmp := _VistaChangeRealfileToIndexfile(tmp);
947
  if not fileexists(tmp) then exit;
948
 
949
  fs := TFileStream.Create(tmp, fmOpenRead);
950
  try
951
    fs.seek(timestamp_vista_position, soFromBeginning);
952
    result := _fileTimeToDateTime(_readInt64(fs));
953
  finally
954
    fs.free;
955
  end;
956
end;
957
{$ENDIF}
958
 
959
function _VistaGetSourceUnicode(infofile: string): string;
960
var
961
  fs: TFileStream;
962
  tmp: string;
963
const
964
  unicode_vista_position = $18;
965
begin
966
  result := '';
967
 
968
  tmp := infofile;
969
  tmp := _VistaChangeRealfileToIndexfile(tmp);
970
  if not fileexists(tmp) then exit;
971
 
972
  fs := TFileStream.Create(tmp, fmOpenRead);
973
  try
974
    fs.seek(unicode_vista_position, soFromBeginning);
975
    result := _readNullTerminatedWideString(fs);
976
  finally
977
    fs.free;
978
  end;
979
end;
980
 
981
function _VistaOriginalSize(infofile: string): integer;
982
var
983
  fs: TFileStream;
984
  tmp: string;
985
const
986
  size_vista_position = $8;
987
begin
988
  result := -1;
989
 
990
  tmp := infofile;
991
  tmp := _VistaChangeRealfileToIndexfile(tmp);
992
  if not fileexists(tmp) then exit;
993
 
994
  fs := TFileStream.Create(tmp, fmOpenRead);
995
  try
996
    fs.seek(size_vista_position, soFromBeginning);
997
    result := _readInt32(fs);
998
  finally
999
    fs.free;
1000
  end;
1001
end;
1002
 
1003
function _checkInfo1or2File(filename: string): boolean;
1004
var
1005
  fs: TStream;
1006
  record_length: integer;
1007
const
1008
  length_position = $C;
1009
  empty_size = 20;
1010
begin
1011
  fs := TFileStream.Create(filename, fmOpenRead);
1012
  try
1013
    fs.seek(length_position, soFromBeginning);
1014
    record_length := _readInt32(fs);
1015
 
1016
    // Check the file length
1017
    if record_length = 0 then
1018
      result := false
1019
    else
1020
      result := (fs.size - empty_size) mod record_length = 0;
1021
  finally
1022
    fs.free;
1023
  end;
1024
end;
1025
 
1026
function _VistaIsValid(infofile: string): boolean;
1027
var
1028
  tmp: string;
1029
begin
1030
  result := false;
1031
 
1032
  tmp := infofile;
1033
  tmp := _VistaChangeRealfileToIndexfile(tmp);
1034
  if not fileexists(tmp) then exit;
1035
 
25 daniel-mar 1036
  // Check the file length
30 daniel-mar 1037
  result := _FileSize(tmp) = vista_valid_index_size;
12 daniel-mar 1038
end;
1039
 
1040
// **********************************************************
1041
// PUBLIC FUNCTIONS
1042
// **********************************************************
1043
 
1044
{$IFDEF DEL6UP}
1045
 
1046
function RecyclerGetDateTime(InfofileOrRecycleFolder: string): tdatetime; overload;
1047
begin
1048
  result := RecyclerGetDateTime(InfofileOrRecycleFolder, '');
1049
end;
1050
 
1051
function RecyclerGetDateTime(drive: char; fileid: string): tdatetime; overload;
1052
begin
1053
  result := RecyclerGetDateTime(drive, '', fileid);
1054
end;
1055
 
1056
function RecyclerGetDateTime(drive: char; UserSID: string; fileid: string): tdatetime; overload;
1057
var
1058
  infofile: string;
1059
begin
1060
  infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1061
  result := RecyclerGetDateTime(infofile, fileid);
1062
end;
1063
 
1064
function RecyclerGetDateTime(InfofileOrRecycleFolder: string; id: string): tdatetime; overload;
1065
var
1066
  fs: TFileStream;
1067
  i, record_length: integer;
1068
  tmp: string;
1069
const
1070
  length_position = $C;
1071
  unique_index_position = $118;
1072
  timestamp_position = $120;
1073
begin
1074
  // FILETIME does start at 01.01.1601 00:00:00 (GMT)
1075
  result := EncodeDateTime(1601, 1, 1, 0, 0, 0, 0);
1076
 
1077
  tmp := InfofileOrRecycleFolder;
1078
 
1079
  if _isFileVistaNamed(tmp) then
1080
  begin
1081
    result := _VistaGetDateTime(tmp);
1082
    exit;
1083
  end;
1084
 
1085
  {$IFDEF allow_all_filenames}
1086
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1087
  begin
1088
    if fileexists(extractfilepath(tmp)+'INFO2') then
1089
      tmp := extractfilepath(tmp)+'INFO2'
1090
    else if fileexists(extractfilepath(tmp)+'INFO') then
1091
      tmp := extractfilepath(tmp)+'INFO';
1092
  end;
1093
  {$ENDIF}
1094
 
1095
  if directoryexists(tmp) then
1096
  begin
1097
    tmp := IncludeTrailingBackslash(tmp);
1098
 
1099
    if fileexists(tmp+'$I'+id) then
1100
    begin
1101
      result := _VistaGetDateTime(tmp+'$I'+id);
1102
      exit;
1103
    end
1104
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1105
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1106
  end;
1107
 
1108
  if not fileexists(tmp) then exit;
1109
  if not RecyclerIsValid(tmp) then exit;
1110
 
1111
  fs := TFileStream.Create(tmp, fmOpenRead);
1112
  try
1113
    fs.seek(length_position, soFromBeginning);
1114
    record_length := _readInt32(fs);
1115
 
1116
    i := -1;
1117
    repeat
1118
      inc(i);
1119
      if unique_index_position+i*record_length > fs.size then break;
1120
      fs.seek(unique_index_position+i*record_length, soFromBeginning);
1121
      if inttostr(_readInt32(fs)) = id then
1122
      begin
1123
        fs.seek(timestamp_position+i*record_length, soFromBeginning);
1124
        result := _fileTimeToDateTime(_readInt64(fs));
1125
        break;
1126
      end;
1127
      until false;
1128
  finally
1129
    fs.free;
1130
  end;
1131
end;
1132
 
1133
{$ENDIF}
1134
 
1135
////////////////////////////////////////////////////////////////////////////////
1136
 
1137
function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string): WideString; overload;
1138
begin
1139
  result := RecyclerGetSourceUnicode(InfofileOrRecycleFolder, '');
1140
end;
1141
 
1142
function RecyclerGetSourceUnicode(drive: char; fileid: string): WideString; overload;
1143
begin
1144
  result := RecyclerGetSourceUnicode(drive, '', fileid);
1145
end;
1146
 
1147
function RecyclerGetSourceUnicode(drive: char; UserSID: string; fileid: string): WideString; overload;
1148
var
1149
  infofile: string;
1150
begin
1151
  if Win32Platform = VER_PLATFORM_WIN32_NT then
1152
  begin
1153
    infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1154
    result := RecyclerGetSourceUnicode(infofile, fileid);
1155
  end
1156
  else
1157
  begin
1158
    // Windows 9x does not support unicode
1159
    result := RecyclerGetSource(drive, UserSID, fileid);
1160
  end;
1161
end;
1162
 
1163
function RecyclerGetSourceUnicode(InfofileOrRecycleFolder: string; id: string): WideString; overload;
1164
var
1165
  fs: TFileStream;
1166
  i, record_length: integer;
1167
  tmp: string;
1168
const
1169
  length_position = $C;
1170
  unique_index_position = $118;
1171
  unicode_source_position = $12C;
1172
begin
1173
  result := '';
1174
 
1175
  tmp := InfofileOrRecycleFolder;
1176
 
1177
  if _isFileVistaNamed(tmp) then
1178
  begin
1179
    // Vista only gives unicode names
1180
    result := _VistaGetSourceUnicode(tmp);
1181
    exit;
1182
  end;
1183
 
1184
  {$IFDEF allow_all_filenames}
1185
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1186
  begin
1187
    if fileexists(extractfilepath(tmp)+'INFO2') then
1188
      tmp := extractfilepath(tmp)+'INFO2'
1189
    else if fileexists(extractfilepath(tmp)+'INFO') then
1190
      tmp := extractfilepath(tmp)+'INFO';
1191
  end;
1192
  {$ENDIF}
1193
 
1194
  if directoryexists(tmp) then
1195
  begin
1196
    tmp := IncludeTrailingBackslash(tmp);
1197
 
1198
    if fileexists(tmp+'$I'+id) then
1199
    begin
1200
      // Vista only gives unicode names
1201
      result := _VistaGetSourceUnicode(tmp+'$I'+id);
1202
      exit;
1203
    end
1204
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1205
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1206
  end;
1207
 
1208
  if not fileexists(tmp) then exit;
1209
  if not RecyclerIsValid(tmp) then exit;
1210
 
1211
  fs := TFileStream.Create(tmp, fmOpenRead);
1212
  try
1213
    fs.seek(length_position, soFromBeginning);
1214
    record_length := _readInt32(fs);
1215
 
1216
    if record_length <> $118 then
1217
    begin
1218
      // Windows NT
1219
      i := -1;
1220
      repeat
1221
        inc(i);
1222
        if unique_index_position+i*record_length > fs.size then break;
1223
        fs.seek(unique_index_position+i*record_length, soFromBeginning);
1224
        if inttostr(_readInt32(fs)) = id then
1225
        begin
1226
          fs.seek(unicode_source_position+i*record_length, soFromBeginning);
1227
          result := _readNullTerminatedWideString(fs);
1228
          break;
1229
        end;
1230
      until false;
1231
    end;
1232
  finally
1233
    fs.free;
1234
  end;
1235
 
1236
  if record_length = $118 then
1237
  begin
1238
    // Windows 9x has no unicode support
1239
    result := RecyclerGetSource(tmp, id);
1240
  end;
1241
end;
1242
 
1243
////////////////////////////////////////////////////////////////////////////////
1244
 
1245
function RecyclerGetSource(InfofileOrRecycleFolder: string): string; overload;
1246
begin
1247
  result := RecyclerGetSource(InfofileOrRecycleFolder, '');
1248
end;
1249
 
1250
function RecyclerGetSource(drive: char; fileid: string): string; overload;
1251
begin
1252
  result := RecyclerGetSource(drive, '', fileid);
1253
end;
1254
 
1255
function RecyclerGetSource(drive: char; UserSID: string; fileid: string): string; overload;
1256
var
1257
  infofile: string;
1258
begin
1259
  infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1260
  result := RecyclerGetSource(infofile, fileid);
1261
end;
1262
 
1263
function RecyclerGetSource(InfofileOrRecycleFolder: string; id: string): string; overload;
1264
var
1265
  fs: TFileStream;
1266
  i, record_length: integer;
1267
  tmp: string;
1268
  alternativ: string;
1269
const
1270
  length_position = $C;
1271
  unique_index_position = $118;
1272
  source_position = $14;
1273
begin
1274
  result := '';
1275
 
1276
  tmp := InfofileOrRecycleFolder;
1277
 
1278
  if _isFileVistaNamed(tmp) then
1279
  begin
1280
    // Vista only gives unicode names
1281
    result := _VistaGetSourceUnicode(tmp);
1282
    exit;
1283
  end;
1284
 
1285
  {$IFDEF allow_all_filenames}
1286
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1287
  begin
1288
    if fileexists(extractfilepath(tmp)+'INFO2') then
1289
      tmp := extractfilepath(tmp)+'INFO2'
1290
    else if fileexists(extractfilepath(tmp)+'INFO') then
1291
      tmp := extractfilepath(tmp)+'INFO';
1292
  end;
1293
  {$ENDIF}
1294
 
1295
  if directoryexists(tmp) then
1296
  begin
1297
    tmp := IncludeTrailingBackslash(tmp);
1298
 
1299
    if fileexists(tmp+'$I'+id) then
1300
    begin
1301
      // Vista only gives unicode names
1302
      result := _VistaGetSourceUnicode(tmp+'$I'+id);
1303
      exit;
1304
    end
1305
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1306
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1307
  end;
1308
 
1309
  if not fileexists(tmp) then exit;
1310
  if not RecyclerIsValid(tmp) then exit;
1311
 
1312
  fs := TFileStream.Create(tmp, fmOpenRead);
1313
  try
1314
    fs.seek(length_position, soFromBeginning);
1315
    record_length := _readInt32(fs);
1316
 
1317
    i := -1;
1318
    repeat
1319
      inc(i);
1320
      if unique_index_position+i*record_length > fs.size then break;
1321
      fs.seek(unique_index_position+i*record_length, soFromBeginning);
1322
      if inttostr(_readInt32(fs)) = id then
1323
      begin
1324
        fs.seek(source_position+i*record_length, soFromBeginning);
1325
        alternativ := _readChar(fs);
1326
 
1327
        if alternativ = #0 then
1328
        begin
1329
          fs.seek(source_position+i*record_length+1, soFromBeginning);
1330
          result := _readNullTerminatedString(fs);
1331
        end
1332
        else
1333
        begin
1334
          fs.seek(source_position+i*record_length, soFromBeginning);
1335
          result := _readNullTerminatedString(fs);
1336
        end;
1337
 
1338
        break;
1339
      end;
1340
    until false;
1341
  finally
1342
    fs.free;
1343
  end;
1344
 
1345
  // In some cases the ansi-source-name is [Null]:\...\
1346
  if alternativ = #0 then
1347
  begin
1348
    result := RecyclerGetSourceDrive(InfofileOrRecycleFolder, id) + result;
1349
  end;
1350
end;
1351
 
1352
////////////////////////////////////////////////////////////////////////////////
1353
 
1354
procedure RecyclerListIndexes(drive: char; result: TStringList); overload;
1355
begin
1356
  RecyclerListIndexes(drive, '', result);
1357
end;
1358
 
1359
procedure RecyclerListIndexes(drive: char; UserSID: string; result: TStringList); overload;
1360
var
1361
  infofile: string;
1362
begin
1363
  infofile := RecyclerGetPath(drive, UserSID, false);
1364
  RecyclerListIndexes(infofile, result);
1365
end;
1366
 
1367
procedure RecyclerListIndexes(InfofileOrRecycleFolder: string; result: TStringList); overload;
1368
var
1369
  fs: TFileStream;
1370
  i, record_length: integer;
1371
  tmp: string;
1372
const
1373
  length_position = $C;
1374
  unique_index_position = $118;
1375
begin
1376
  tmp := InfofileOrRecycleFolder;
1377
 
1378
  if _isFileVistaNamed(tmp) then
1379
  begin
1380
    _VistaListIndexes(extractfilepath(tmp), result);
1381
    exit;
1382
  end;
1383
 
1384
  {$IFDEF allow_all_filenames}
1385
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1386
  begin
1387
    if fileexists(extractfilepath(tmp)+'INFO2') then
1388
      tmp := extractfilepath(tmp)+'INFO2'
1389
    else if fileexists(extractfilepath(tmp)+'INFO') then
1390
      tmp := extractfilepath(tmp)+'INFO';
1391
  end;
1392
  {$ENDIF}
1393
 
1394
  if directoryexists(tmp) then
1395
  begin
1396
    tmp := IncludeTrailingBackslash(tmp);
1397
 
1398
    if fileexists(tmp+'INFO2') then     tmp := tmp+'INFO2'
1399
    else if fileexists(tmp+'INFO') then tmp := tmp+'INFO'
1400
    else
1401
    begin
1402
      // Last try: is it a vista-directory?
1403
      _VistaListIndexes(tmp, result);
1404
      exit;
1405
    end;
1406
  end;
1407
 
1408
  if not fileexists(tmp) then exit;
1409
  if not RecyclerIsValid(tmp) then exit;
1410
 
1411
  fs := TFileStream.Create(tmp, fmOpenRead);
1412
  try
1413
    fs.seek(length_position, soFromBeginning);
1414
    record_length := _readInt32(fs);
1415
 
1416
    i := -1;
1417
    repeat
1418
      inc(i);
1419
      if unique_index_position+i*record_length > fs.size then break;
1420
      fs.seek(unique_index_position+i*record_length, soFromBeginning);
1421
 
1422
      result.Add(inttostr(_readInt32(fs)));
1423
    until false;
1424
  finally
1425
    fs.free;
1426
  end;
1427
end;
1428
 
1429
////////////////////////////////////////////////////////////////////////////////
1430
 
1431
function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string): char; overload;
1432
begin
1433
  result := RecyclerGetSourceDrive(InfofileOrRecycleFolder, '');
1434
end;
1435
 
1436
function RecyclerGetSourceDrive(drive: char; fileid: string): char; overload;
1437
begin
1438
  result := RecyclerGetSourceDrive(drive, '', fileid);
1439
end;
1440
 
1441
function RecyclerGetSourceDrive(drive: char; UserSID: string; fileid: string): char; overload;
1442
var
1443
  infofile: string;
1444
begin
1445
  infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1446
  result := RecyclerGetSourceDrive(infofile, fileid);
1447
end;
1448
 
1449
function RecyclerGetSourceDrive(InfofileOrRecycleFolder: string; id: string): char; overload;
1450
var
1451
  fs: TFileStream;
1452
  i, record_length: integer;
1453
  tmp: string;
1454
const
1455
  length_position = $C;
1456
  unique_index_position = $118;
1457
  source_drive_position = $11C;
1458
begin
1459
  result := #0;
1460
 
1461
  tmp := InfofileOrRecycleFolder;
1462
 
1463
  if _isFileVistaNamed(tmp) then
1464
  begin
1465
    result := _VistaGetSourceDrive(tmp);
1466
    exit;
1467
  end;
1468
 
1469
  {$IFDEF allow_all_filenames}
1470
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1471
  begin
1472
    if fileexists(extractfilepath(tmp)+'INFO2') then
1473
      tmp := extractfilepath(tmp)+'INFO2'
1474
    else if fileexists(extractfilepath(tmp)+'INFO') then
1475
      tmp := extractfilepath(tmp)+'INFO';
1476
  end;
1477
  {$ENDIF}
1478
 
1479
  if directoryexists(tmp) then
1480
  begin
1481
    tmp := IncludeTrailingBackslash(tmp);
1482
 
1483
    if fileexists(tmp+'$I'+id) then
1484
    begin
1485
      result := _VistaGetSourceDrive(tmp+'$I'+id);
1486
      exit;
1487
    end
1488
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1489
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1490
  end;
1491
 
1492
  if not fileexists(tmp) then exit;
1493
  if not RecyclerIsValid(tmp) then exit;
1494
 
1495
  fs := TFileStream.Create(tmp, fmOpenRead);
1496
  try
1497
    fs.seek(length_position, soFromBeginning);
1498
    record_length := _readInt32(fs);
1499
 
1500
    i := -1;
1501
    repeat
1502
      inc(i);
1503
      if unique_index_position+i*record_length > fs.size then break;
1504
      fs.seek(unique_index_position+i*record_length, soFromBeginning);
1505
      if inttostr(_readInt32(fs)) = id then
1506
      begin
1507
        fs.seek(source_drive_position+i*record_length, soFromBeginning);
1508
        result := chr(ord('A') + _readInt8(fs));
1509
        break;
1510
      end;
1511
    until false;
1512
  finally
1513
    fs.free;
1514
  end;
1515
end;
1516
 
1517
////////////////////////////////////////////////////////////////////////////////
1518
 
1519
function RecyclerOriginalSize(InfofileOrRecycleFolder: string): integer; overload;
1520
begin
1521
  result := RecyclerOriginalSize(InfofileOrRecycleFolder, '');
1522
end;
1523
 
1524
function RecyclerOriginalSize(drive: char; fileid: string): integer; overload;
1525
begin
1526
  result := RecyclerOriginalSize(drive, '', fileid);
1527
end;
1528
 
1529
function RecyclerOriginalSize(drive: char; UserSID: string; fileid: string): integer; overload;
1530
var
1531
  infofile: string;
1532
begin
1533
  infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1534
  result := RecyclerOriginalSize(infofile, fileid);
1535
end;
1536
 
1537
function RecyclerOriginalSize(InfofileOrRecycleFolder: string; id: string): integer; overload;
1538
var
1539
  fs: TFileStream;
1540
  i, record_length: integer;
1541
  tmp: string;
1542
const
1543
  length_position = $C;
1544
  unique_index_position = $118;
1545
  original_size_position = $128;
1546
begin
1547
  result := -1;
1548
 
1549
  tmp := InfofileOrRecycleFolder;
1550
 
1551
  if _isFileVistaNamed(tmp) then
1552
  begin
1553
    result := _VistaOriginalSize(tmp);
1554
    exit;
1555
  end;
1556
 
1557
  {$IFDEF allow_all_filenames}
1558
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1559
  begin
1560
    if fileexists(extractfilepath(tmp)+'INFO2') then
1561
      tmp := extractfilepath(tmp)+'INFO2'
1562
    else if fileexists(extractfilepath(tmp)+'INFO') then
1563
      tmp := extractfilepath(tmp)+'INFO';
1564
  end;
1565
  {$ENDIF}
1566
 
1567
  if directoryexists(tmp) then
1568
  begin
1569
    tmp := IncludeTrailingBackslash(tmp);
1570
 
1571
    if fileexists(tmp+'$I'+id) then
1572
    begin
1573
      result := _VistaOriginalSize(tmp+'$I'+id);
1574
      exit;
1575
    end
1576
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1577
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1578
  end;
1579
 
1580
  if not fileexists(tmp) then exit;
1581
  if not RecyclerIsValid(tmp) then exit;
1582
 
1583
  fs := TFileStream.Create(tmp, fmOpenRead);
1584
  try
1585
    fs.seek(length_position, soFromBeginning);
1586
    record_length := _readInt32(fs);
1587
 
1588
    i := -1;
1589
    repeat
1590
      inc(i);
1591
      if unique_index_position+i*record_length > fs.size then break;
1592
      fs.seek(unique_index_position+i*record_length, soFromBeginning);
1593
      if inttostr(_readInt32(fs)) = id then
1594
      begin
1595
        fs.seek(original_size_position+i*record_length, soFromBeginning);
1596
        result := _readInt32(fs);
1597
        break;
1598
      end;
1599
    until false;
1600
  finally
1601
    fs.free;
1602
  end;
1603
end;
1604
 
1605
////////////////////////////////////////////////////////////////////////////////
1606
 
1607
function RecyclerIsValid(drive: char): boolean; overload;
1608
begin
67 daniel-mar 1609
  // Bei Vista und Win2003 (VM) erhalte ich bei LW A: die Meldung
23 daniel-mar 1610
  // "c0000013 Kein Datenträger". Exception Abfangen geht nicht.
1611
  // Daher erstmal überprüfen, ob Laufwerk existiert.
1612
  result := false;
1613
  if not RecyclerIsPossible(drive) then exit;
1614
 
12 daniel-mar 1615
  result := RecyclerIsValid(drive, '');
1616
end;
1617
 
1618
function RecyclerIsValid(drive: char; UserSID: string): boolean; overload;
1619
var
1620
  infofile: string;
1621
begin
23 daniel-mar 1622
  // Anmerkung siehe oben.
1623
  result := false;
1624
  if not RecyclerIsPossible(drive) then exit;
1625
 
12 daniel-mar 1626
  infofile := RecyclerGetPath(drive, UserSID, false);
1627
  result := RecyclerIsValid(infofile);
1628
end;
1629
 
1630
function RecyclerIsValid(InfofileOrRecycleFolder: string): boolean; overload;
1631
var
1632
  tmp: string;
1633
  x: TStringList;
1634
  i: integer;
1635
  eine_fehlerhaft: boolean;
1636
begin
1637
  result := false;
1638
 
1639
  tmp := InfofileOrRecycleFolder;
1640
 
1641
  if _isFileVistaNamed(tmp) then
1642
  begin
1643
    result := _VistaIsValid(tmp);
1644
    exit;
1645
  end;
1646
 
1647
  {$IFDEF allow_all_filenames}
1648
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1649
  begin
1650
    if fileexists(extractfilepath(tmp)+'INFO2') then
1651
      tmp := extractfilepath(tmp)+'INFO2'
1652
    else if fileexists(extractfilepath(tmp)+'INFO') then
1653
      tmp := extractfilepath(tmp)+'INFO';
1654
  end;
1655
  {$ENDIF}
1656
 
1657
  if directoryexists(tmp) then
1658
  begin
1659
    tmp := IncludeTrailingBackslash(tmp);
1660
 
1661
    if fileexists(tmp+'INFO2') then
1662
    begin
1663
      result := _checkInfo1or2File(tmp+'INFO2');
1664
    end;
1665
 
1666
    if not result and fileexists(tmp+'INFO') then
1667
    begin
1668
      result := _checkInfo1or2File(tmp+'INFO');
1669
    end;
1670
 
1671
    if not result then
1672
    begin
1673
      // Complete vista-directory declared?
1674
      eine_fehlerhaft := false;
1675
      x := TStringList.Create;
1676
      try
1677
        _VistaListIndexes(tmp, x);
1678
        for i := 0 to x.Count - 1 do
1679
        begin
1680
          if not _VistaIsValid(tmp+'$I'+x.Strings[i]) then
1681
          begin
1682
            eine_fehlerhaft := true;
1683
          end;
1684
        end;
1685
      finally
1686
        x.Free;
1687
      end;
1688
      result := not eine_fehlerhaft;
1689
    end;
1690
  end;
1691
 
1692
  if not fileexists(tmp) then exit;
1693
 
1694
  result := _checkInfo1or2File(tmp);
1695
end;
1696
 
1697
////////////////////////////////////////////////////////////////////////////////
1698
 
1699
function RecyclerCurrentFilename(InfofileOrRecycleFolder: string): string; overload;
1700
begin
1701
  result := RecyclerCurrentFilename(InfofileOrRecycleFolder, '');
1702
end;
1703
 
1704
function RecyclerCurrentFilename(drive: char; fileid: string): string; overload;
1705
begin
1706
  result := RecyclerCurrentFilename(drive, '', fileid);
1707
end;
1708
 
1709
function RecyclerCurrentFilename(drive: char; UserSID: string; fileid: string): string; overload;
1710
var
1711
  infofile: string;
25 daniel-mar 1712
begin
12 daniel-mar 1713
  infofile := RecyclerGetPath(drive, UserSID, true, fileid);
1714
  result := RecyclerCurrentFilename(infofile, fileid);
1715
end;
1716
 
1717
function RecyclerCurrentFilename(InfofileOrRecycleFolder: string; id: string): string; overload;
1718
var
1719
  a, c: string;
1720
  tmp: string;
1721
begin
1722
  result := '';
1723
 
1724
  tmp := InfofileOrRecycleFolder;
1725
 
1726
  if _isFileVistaNamed(tmp) then
1727
  begin
1728
    result := _VistaCurrentFilename(tmp);
1729
    exit;
1730
  end;
1731
 
1732
  {$IFDEF allow_all_filenames}
1733
  if not RecyclerIsValid(tmp) and fileexists(tmp) then
1734
  begin
1735
    if fileexists(extractfilepath(tmp)+'INFO2') then
1736
      tmp := extractfilepath(tmp)+'INFO2'
1737
    else if fileexists(extractfilepath(tmp)+'INFO') then
1738
      tmp := extractfilepath(tmp)+'INFO';
1739
  end;
1740
  {$ENDIF}
1741
 
1742
  if directoryexists(tmp) then
1743
  begin
1744
    tmp := IncludeTrailingBackslash(tmp);
1745
 
1746
    if fileexists(tmp+'$I'+id) then
1747
    begin
1748
      result := _VistaCurrentFilename(tmp+'$I'+id);
1749
      exit;
1750
    end
1751
    else if fileexists(tmp+'INFO2') then tmp := tmp+'INFO2'
1752
    else if fileexists(tmp+'INFO') then  tmp := tmp+'INFO';
1753
  end;
1754
 
1755
  a := RecyclerGetSourceDrive(tmp, id);
1756
  c := extractfileext(RecyclerGetSourceUnicode(tmp, id));
1757
  if (a <> '') then
1758
  begin
1759
    result := 'D' + a + id + c;
1760
  end;
1761
end;
1762
 
1763
////////////////////////////////////////////////////////////////////////////////
1764
 
1765
function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string): string; overload;
1766
var
1767
  sl: TStringList;
1768
begin
1769
  sl := TStringList.Create;
1770
  try
1771
    RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, fileid, sl);
1772
    if sl.Count > 0 then
1773
      result := ExtractFilePath(sl.Strings[0])
1774
    else
1775
      result := '';
1776
  finally
1777
    sl.free;
1778
  end;
1779
end;
1780
 
1781
function RecyclerGetPath(drive: char; UserSID: string; IncludeInfofile: boolean): string; overload;
1782
var
1783
  sl: TStringList;
1784
begin
1785
  sl := TStringList.Create;
1786
  try
1787
    RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, sl);
1788
    if sl.Count > 0 then
1789
      result := ExtractFilePath(sl.Strings[0])
1790
    else
1791
      result := '';
1792
  finally
1793
    sl.free;
1794
  end;
1795
end;
1796
 
1797
function RecyclerGetPath(drive: char; IncludeInfofile: boolean): string; overload;
1798
var
1799
  sl: TStringList;
1800
begin
1801
  sl := TStringList.Create;
1802
  try
1803
    RecyclerGetInfofiles(drive, IncludeInfofile, sl);
1804
    if sl.Count > 0 then
1805
      result := ExtractFilePath(sl.Strings[0])
1806
    else
1807
      result := '';
1808
  finally
1809
    sl.free;
1810
  end;
1811
end;
1812
 
1813
function RecyclerGetPath(drive: char; UserSID: string): string; overload;
1814
var
1815
  sl: TStringList;
1816
begin
1817
  sl := TStringList.Create;
1818
  try
1819
    RecyclerGetInfofiles(drive, UserSID, sl);
1820
    if sl.Count > 0 then
1821
      result := ExtractFilePath(sl.Strings[0])
1822
    else
1823
      result := '';
1824
  finally
1825
    sl.free;
1826
  end;
1827
end;
1828
 
1829
function RecyclerGetPath(drive: char): string; overload;
1830
var
1831
  sl: TStringList;
1832
begin
1833
  sl := TStringList.Create;
1834
  try
1835
    RecyclerGetInfofiles(drive, sl);
1836
    if sl.Count > 0 then
1837
      result := ExtractFilePath(sl.Strings[0])
1838
    else
1839
      result := '';
1840
  finally
1841
    sl.free;
1842
  end;
1843
end;
1844
 
1845
////////////////////////////////////////////////////////////////////////////////
1846
 
1847
procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; fileid: string; result: TStringList); overload;
1848
var
1849
  dir: string;
1850
begin
27 daniel-mar 1851
  // Find recyclers from Windows Vista or higher
1852
 
1853
  if _isFAT(drive) then
1854
  begin
1855
    dir := drive + DriveDelim + PathDelim + '$recycle.bin' + PathDelim;
1856
    if IncludeInfofile and (fileid <> '') then
12 daniel-mar 1857
    begin
27 daniel-mar 1858
      if fileExists(dir + '$I'+fileid) then
12 daniel-mar 1859
      begin
27 daniel-mar 1860
        result.Add(dir + '$I'+fileid);
1861
      end;
1862
    end
1863
    else
1864
    begin
1865
      if directoryExists(dir) then
1866
      begin
1867
        result.Add(dir);
1868
      end;
1869
    end;
1870
  end
1871
  else
1872
  begin
1873
    if UserSID <> '' then
1874
    begin
1875
      dir := drive + DriveDelim + PathDelim + '$recycle.bin'+PathDelim+UserSID+PathDelim;
1876
      if IncludeInfofile and (fileid <> '') then
1877
      begin
1878
        if fileExists(dir + '$I'+fileid) then
12 daniel-mar 1879
        begin
27 daniel-mar 1880
          result.Add(dir + '$I'+fileid);
12 daniel-mar 1881
        end;
1882
      end
1883
      else
1884
      begin
27 daniel-mar 1885
        if directoryExists(dir) then
12 daniel-mar 1886
        begin
27 daniel-mar 1887
          result.Add(dir);
12 daniel-mar 1888
        end;
1889
      end;
1890
    end
1891
    else
1892
    begin
27 daniel-mar 1893
      dir := drive + DriveDelim + PathDelim + '$recycle.bin'+PathDelim+_getMySID()+PathDelim;
1894
      if IncludeInfofile and (fileid <> '') then
12 daniel-mar 1895
      begin
27 daniel-mar 1896
        if fileExists(dir + '$I'+fileid) then
12 daniel-mar 1897
        begin
27 daniel-mar 1898
          result.Add(dir + '$I'+fileid);
12 daniel-mar 1899
        end;
1900
      end
1901
      else
1902
      begin
27 daniel-mar 1903
        if directoryExists(dir) then
12 daniel-mar 1904
        begin
27 daniel-mar 1905
          result.Add(dir);
12 daniel-mar 1906
        end;
1907
      end;
1908
    end;
27 daniel-mar 1909
  end;
1910
 
1911
  // Find recyclers from Windows before Vista
1912
 
1913
  if _isFAT(drive) then
1914
  begin
1915
    dir := drive + DriveDelim + PathDelim + 'Recycled' + PathDelim;
1916
    if IncludeInfofile then
1917
    begin
1918
      // Both "recycle bins" are possible if you have multiboot (but do overwrite themselfes if you empty them)
1919
      if fileExists(dir + 'INFO2') then
1920
        result.Add(dir + 'INFO2'); // Windows 95 with Internet Explorer 4 Extension or higher Windows versions
1921
      if fileExists(dir + 'INFO') then
1922
        result.Add(dir + 'INFO'); // Windows 95 native
1923
    end
1924
    else
1925
    begin
1926
      if directoryExists(dir) then
1927
        result.Add(dir);
1928
    end;
1929
  end
12 daniel-mar 1930
  else
1931
  begin
27 daniel-mar 1932
    if UserSID <> '' then
12 daniel-mar 1933
    begin
27 daniel-mar 1934
      dir := drive + DriveDelim + PathDelim + 'Recycler'+PathDelim+UserSID+PathDelim;
1935
      if IncludeInfofile then
1936
      begin
1937
        if fileExists(dir + 'INFO2') then
1938
          result.Add(dir + 'INFO2');
67 daniel-mar 1939
        if fileExists(dir + 'INFO') then
1940
          result.Add(dir + 'INFO'); // Windows NT 4
27 daniel-mar 1941
      end
1942
      else
1943
      begin
1944
        if directoryExists(dir) then
1945
          result.Add(dir);
1946
      end;
12 daniel-mar 1947
    end
1948
    else
1949
    begin
27 daniel-mar 1950
      dir := drive + DriveDelim + PathDelim + 'Recycler'+PathDelim+_getMySID()+PathDelim;
1951
      if IncludeInfofile then
1952
      begin
1953
        if fileExists(dir + 'INFO2') then
1954
          result.Add(dir + 'INFO2');
67 daniel-mar 1955
        if fileExists(dir + 'INFO') then
1956
          result.Add(dir + 'INFO'); // Windows NT 4
27 daniel-mar 1957
      end
1958
      else
1959
      begin
1960
        if directoryExists(dir) then
1961
          result.Add(dir);
1962
      end;
12 daniel-mar 1963
    end;
27 daniel-mar 1964
  end;
12 daniel-mar 1965
end;
1966
 
1967
procedure RecyclerGetInfofiles(drive: char; UserSID: string; IncludeInfofile: boolean; result: TStringList); overload;
1968
begin
1969
  RecyclerGetInfofiles(drive, UserSID, IncludeInfofile, '', result);
1970
end;
1971
 
1972
procedure RecyclerGetInfofiles(drive: char; IncludeInfofile: boolean; result: TStringList); overload;
1973
begin
1974
  RecyclerGetInfofiles(drive, '', IncludeInfofile, '', result);
1975
end;
1976
 
1977
procedure RecyclerGetInfofiles(drive: char; UserSID: string; result: TStringList); overload;
1978
begin
1979
  RecyclerGetInfofiles(drive, UserSID, false, '', result);
1980
end;
1981
 
1982
procedure RecyclerGetInfofiles(drive: char; result: TStringList); overload;
1983
begin
1984
  RecyclerGetInfofiles(drive, '', false, '', result);
1985
end;
1986
 
1987
////////////////////////////////////////////////////////////////////////////////
1988
 
1989
function RecyclerCurrentFilenameAndPath(drive: char; UserSID: string; fileid: string): string; overload;
1990
begin
1991
  result := RecyclerGetPath(drive, UserSID, false, fileid) +
1992
    RecyclerCurrentFilename(drive, UserSID, fileid);
1993
end;
1994
 
1995
function RecyclerCurrentFilenameAndPath(drive: char; fileid: string): string; overload;
1996
begin
1997
  result := RecyclerCurrentFilenameAndPath(drive, '', fileid);
1998
end;
1999
 
2000
function RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder: string; id: string): string; overload;
2001
begin
2002
  if RecyclerIsValid(InfofileOrRecycleFolder) then
2003
  begin
2004
    result := extractfilepath(InfofileOrRecycleFolder) +
2005
      RecyclerCurrentFilename(InfofileOrRecycleFolder, id);
2006
  end
2007
  else
2008
    result := '';
2009
end;
2010
 
2011
////////////////////////////////////////////////////////////////////////////////
2012
 
2013
function RecyclerRemoveItem(drive: char; UserSID: string; fileid: string): boolean; overload;
2014
var
2015
  tmp: string;
2016
begin
2017
  tmp := RecyclerCurrentFilenameAndPath(drive, UserSID, fileid);
2018
  if fileexists(tmp) then
2019
  begin
2020
    deletefile(tmp);
2021
    result := fileexists(tmp);
2022
  end
2023
  else
2024
  begin
2025
    directoryexists(tmp);
2026
    result := directoryexists(tmp);
2027
  end;
2028
end;
2029
 
2030
function RecyclerRemoveItem(drive: char; fileid: string): boolean; overload;
2031
begin
2032
  result := RecyclerRemoveItem(drive, '', fileid);
2033
end;
2034
 
2035
function RecyclerRemoveItem(InfofileOrRecycleFolder: string; id: string): boolean; overload;
2036
var
2037
  tmp: string;
2038
begin
2039
  tmp := RecyclerCurrentFilenameAndPath(InfofileOrRecycleFolder, id);
2040
  if fileexists(tmp) then
2041
  begin
2042
    deletefile(tmp);
2043
    result := fileexists(tmp);
2044
  end
2045
  else
2046
  begin
2047
    directoryexists(tmp);
2048
    result := directoryexists(tmp);
2049
  end;
2050
end;
2051
 
2052
procedure RecyclerGetAllRecyclerDrives(result: TStringList);
2053
var
25 daniel-mar 2054
  Drive: char;
12 daniel-mar 2055
begin
25 daniel-mar 2056
  for Drive := 'A' to 'Z' do
12 daniel-mar 2057
  begin
25 daniel-mar 2058
    if RecyclerIsPossible(Drive) and RecyclerIsValid(Drive) then
12 daniel-mar 2059
    begin
25 daniel-mar 2060
      result.Add(Drive);
12 daniel-mar 2061
    end;
2062
  end;
2063
end;
2064
 
2065
////////////////////////////////////////////////////////////////////////////////
2066
 
2067
// http://www.dsdt.info/tipps/?id=176
2068
function RecyclerEmptyRecycleBin(flags: cardinal): boolean; overload;
2069
type
2070
  TSHEmptyRecycleBin = function (Wnd: HWND;
2071
                                 pszRootPath: PChar;
2072
                                 dwFlags: DWORD):
2073
                                 HRESULT; stdcall;
2074
var
2075
  PSHEmptyRecycleBin: TSHEmptyRecycleBin;
2076
  LibHandle: THandle;
2077
const
2078
  C_SHEmptyRecycleBinA = 'SHEmptyRecycleBinA';
2079
begin
2080
  result := true;
2081
  LibHandle := LoadLibrary(shell32) ;
2082
  try
2083
    if LibHandle <> 0 then
2084
    begin
2085
      @PSHEmptyRecycleBin:= GetProcAddress(LibHandle, C_SHEmptyRecycleBinA);
2086
      if @PSHEmptyRecycleBin <> nil then
2087
      begin
2088
        PSHEmptyRecycleBin(hInstance, nil, flags);
2089
      end
2090
      else
2091
        result := false;
2092
    end
2093
    else
2094
      result := false;
2095
  finally
2096
    @PSHEmptyRecycleBin := nil;
2097
    FreeLibrary(LibHandle);
2098
  end;
2099
end;
2100
 
2101
function RecyclerEmptyRecycleBin(sound, progress, confirmation: boolean): boolean; overload;
2102
const
2103
  SHERB_NOCONFIRMATION = $00000001;
2104
  SHERB_NOPROGRESSUI   = $00000002;
2105
  SHERB_NOSOUND        = $00000004;
2106
var
2107
  flags: cardinal;
2108
begin
2109
  flags := 0;
2110
 
2111
  if not progress then
2112
    flags := flags or SHERB_NOPROGRESSUI;
2113
  if not confirmation then
2114
    flags := flags or SHERB_NOCONFIRMATION;
2115
  if not sound then
2116
    flags := flags or SHERB_NOSOUND;
2117
 
2118
  result := RecyclerEmptyRecycleBin(flags);
2119
end;
2120
 
2121
////////////////////////////////////////////////////////////////////////////////
2122
 
2123
// Template
2124
// http://www.dsdt.info/tipps/?id=116
2125
function RecyclerAddFileOrFolder(FileOrFolder: string; confirmation: boolean): boolean; overload;
2126
var
2127
  Operation: TSHFileOpStruct;
2128
begin
2129
  with Operation do
2130
  begin
2131
    Wnd := hInstance; // OK?
2132
    wFunc := FO_DELETE;
2133
    pFrom := PChar(FileOrFolder + #0);
2134
    pTo := nil;
2135
    fFlags := FOF_ALLOWUNDO;
2136
    if not confirmation then fFlags := fFlags or FOF_NOCONFIRMATION;
2137
  end;
2138
  Result := SHFileOperation(Operation) = 0;
2139
end;
2140
 
2141
function RecyclerAddFileOrFolder(FileOrFolder: string): boolean; overload;
2142
begin
2143
  result := RecyclerAddFileOrFolder(FileOrFolder, false);
2144
end;
2145
 
2146
function RecyclerConfirmationDialogEnabled: boolean;
17 daniel-mar 2147
var
2148
  gp: GPOLICYBOOL;
2149
begin
2150
  gp := RecyclerGroupPolicyConfirmFileDelete;
2151
  if gp <> gpUndefined then
2152
  begin
2153
    result := gp = gpEnabled;
2154
  end
2155
  else
2156
  begin
2157
    result := RecyclerShellStateConfirmationDialogEnabled;
2158
  end;
2159
end;
2160
 
2161
function RecyclerShellStateConfirmationDialogEnabled: boolean;
12 daniel-mar 2162
type
2163
  TSHGetSettings = procedure (var lpss: SHELLSTATE; dwMask: DWORD) stdcall;
2164
const
2165
  C_SHGetSettings = 'SHGetSettings';
2166
var
2167
  lpss: SHELLSTATE;
2168
  bNoConfirmRecycle: boolean;
2169
 
2170
  PSHGetSettings: TSHGetSettings;
2171
  RBHandle: THandle;
2172
 
2173
  reg: TRegistry;
2174
  rbuf: array[0..255] of byte;
2175
begin
2176
  PSHGetSettings := nil;
2177
  result := false; // Avoid warning message
2178
 
2179
  RBHandle := LoadLibrary(shell32);
2180
  if(RBHandle <> 0) then
2181
  begin
2182
    PSHGetSettings := GetProcAddress(RBHandle, C_SHGetSettings);
2183
    if (@PSHGetSettings = nil) then
2184
    begin
2185
      FreeLibrary(RBHandle);
2186
      RBHandle := 0;
2187
    end;
2188
  end;
2189
 
2190
  if (RBHandle <> 0) and (Assigned(PSHGetSettings)) then
2191
  begin
2192
    ZeroMemory(@lpss, SizeOf(lpss));
2193
    PSHGetSettings(lpss, SSF_NOCONFIRMRECYCLE);
2194
    // bNoConfirmRecycle := (lpss.Flags1 and 4) = 4; // fNoConfirmRecycle
2195
    bNoConfirmRecycle := GetByteBit(lpss.Flags1, 2);
2196
 
2197
    result := not bNoConfirmRecycle;
2198
  end
2199
  else
2200
  begin
2201
    reg := TRegistry.Create;
2202
    try
2203
      // API function call failed. Probably because Windows is too old.
2204
      // Try to read out from registry.
16 daniel-mar 2205
      // The 3rd bit of the 5th byte of "ShellState" is the value
2206
      // of "fNoConfirmRecycle".
12 daniel-mar 2207
 
2208
      reg.RootKey := HKEY_CURRENT_USER;
2209
      if (reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Explorer')) then
2210
      begin
2211
        ZeroMemory(@rbuf, SizeOf(rbuf));
2212
        reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf));
2213
 
2214
        // Lese 3tes Bit vom 5ten Byte
2215
        // bNoConfirmRecycle := ((rbuf[4] and 4) = 4);
30 daniel-mar 2216
        bNoConfirmRecycle := GetByteBit(rbuf[4], 2);
12 daniel-mar 2217
        result := not bNoConfirmRecycle;
2218
 
2219
        reg.CloseKey;
2220
      end
2221
      else
2222
      begin
2223
        raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSettings])]);
2224
      end;
2225
    finally
2226
      reg.Free;
2227
    end;
2228
  end;
2229
 
2230
  if (RBHandle <> 0) then FreeLibrary(RBHandle);
2231
end;
2232
 
2233
procedure RecyclerConfirmationDialogSetEnabled(NewSetting: boolean);
2234
type
2235
  TSHGetSetSettings = procedure (var lpss: SHELLSTATE; dwMask: DWORD; bSet: BOOL) stdcall;
2236
const
2237
  C_SHGetSetSettings = 'SHGetSetSettings';
2238
var
2239
  lpss: SHELLSTATE;
2240
 
2241
  PSHGetSetSettings: TSHGetSetSettings;
2242
  RBHandle: THandle;
2243
 
2244
  reg: TRegistry;
2245
  rbuf: array[0..255] of byte;
2246
 
2247
  dwResult: DWORD;
2248
begin
2249
  PSHGetSetSettings := nil;
2250
 
2251
  RBHandle := LoadLibrary(shell32);
2252
  if(RBHandle <> 0) then
2253
  begin
2254
    PSHGetSetSettings := GetProcAddress(RBHandle, C_SHGetSetSettings);
2255
    if (@PSHGetSetSettings = nil) then
2256
    begin
2257
      FreeLibrary(RBHandle);
2258
      RBHandle := 0;
2259
    end;
2260
  end;
2261
 
2262
  if (RBHandle <> 0) and (Assigned(PSHGetSetSettings)) then
2263
  begin
2264
    ZeroMemory(@lpss, SizeOf(lpss));
2265
    PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, false); // Get
2266
    lpss.Flags1 := SetByteBit(lpss.Flags1, 2, NewSetting);
2267
    PSHGetSetSettings(lpss, SSF_NOCONFIRMRECYCLE, true); // Set
2268
 
2269
    SendMessageTimeout (
2270
      HWND_BROADCAST, WM_SETTINGCHANGE,
19 daniel-mar 2271
      0, lParam (pChar ('ShellState')),
12 daniel-mar 2272
      SMTO_ABORTIFHUNG, 5000, dwResult
2273
    );
2274
  end
2275
  else
2276
  begin
2277
    reg := TRegistry.Create;
2278
    try
2279
      // API function call failed. Probably because Windows is too old.
2280
      // Try to read out from registry.
16 daniel-mar 2281
      // The 3rd bit of the 5th byte of "ShellState" is the value
2282
      // of "fNoConfirmRecycle".
12 daniel-mar 2283
 
2284
      reg.RootKey := HKEY_CURRENT_USER;
2285
      if (reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer', false)) then
2286
      begin
2287
        ZeroMemory(@rbuf, SizeOf(rbuf));
2288
        reg.ReadBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Get
2289
        rbuf[4] := SetByteBit(rbuf[4], 2, NewSetting);
2290
        reg.WriteBinaryData('ShellState', rbuf, SizeOf(rbuf)); // Set
2291
 
2292
        SendMessageTimeout (
2293
          HWND_BROADCAST, WM_SETTINGCHANGE,
19 daniel-mar 2294
          0, lParam (pChar ('ShellState')),
12 daniel-mar 2295
          SMTO_ABORTIFHUNG, 5000, dwResult
2296
        );
2297
 
2298
        reg.CloseKey;
2299
      end
2300
      else
2301
      begin
2302
        raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHGetSetSettings])]);
2303
      end;
2304
    finally
2305
      reg.Free;
2306
    end;
2307
  end;
2308
 
2309
  if (RBHandle <> 0) then FreeLibrary(RBHandle);
2310
end;
2311
 
2312
function RecyclerGetCurrentIconString: string;
2313
begin
2314
  if RecyclerIsEmpty then
2315
    result := RecyclerGetEmptyIconString
2316
  else
2317
    result := RecyclerGetFullIconString;
2318
end;
2319
 
2320
function RecyclerGetDefaultIconString: string;
2321
var
2322
  reg: TRegistry;
2323
begin
2324
  // Please note: The "default" icon is not always the icon of the
2325
  // current recycle bin in its current state (full, empty)
2326
  // At Windows 95b, the registry value actually did change every time the
2327
  // recycle bin state did change, but at Windows 2000 I could not see any
2328
  // update, even after reboot. So, the registry value is possible fixed as
2329
  // default = empty on newer OS versions.
2330
 
2331
  reg := TRegistry.Create;
2332
  try
2333
    reg.RootKey := HKEY_CLASSES_ROOT;
2334
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
2335
    begin
2336
      result := reg.ReadString('');
2337
      reg.CloseKey;
2338
    end;
2339
  finally
2340
    reg.Free;
2341
  end;
2342
end;
2343
 
2344
function RecyclerGetEmptyIconString: string;
2345
var
2346
  reg: TRegistry;
2347
begin
2348
  reg := TRegistry.Create;
2349
  try
2350
    reg.RootKey := HKEY_CLASSES_ROOT;
2351
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
2352
    begin
2353
      result := reg.ReadString('Empty');
2354
      reg.CloseKey;
2355
    end;
2356
  finally
2357
    reg.Free;
2358
  end;
2359
end;
2360
 
2361
function RecyclerGetFullIconString: string;
2362
var
2363
  reg: TRegistry;
2364
begin
2365
  reg := TRegistry.Create;
2366
  try
2367
    reg.RootKey := HKEY_CLASSES_ROOT;
2368
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID+'\DefaultIcon') then
2369
    begin
2370
      result := reg.ReadString('Full');
2371
      reg.CloseKey;
2372
    end;
2373
  finally
2374
    reg.Free;
2375
  end;
2376
end;
2377
 
2378
function RecyclerGetName: string;
2379
var
2380
  reg: TRegistry;
2381
begin
2382
  // Windows 95b:
2383
  // Change of CLSID\{645FF040-5081-101B-9F08-00AA002F954E} will change the desktop name of the recycle bin.
2384
 
2385
  // Windows 2000: If LocalizedString is available, the 3rd argument will be parsed
2386
  // (if the third argument will removed, it will be read out from the DLL resource string automatically)
2387
 
2388
  reg := TRegistry.Create;
2389
  try
2390
    reg.RootKey := HKEY_CLASSES_ROOT;
2391
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
2392
    begin
2393
      if reg.ValueExists('LocalizedString') then
2394
      begin
2395
        result := reg.ReadString('LocalizedString');
2396
        result := _DecodeReferenceString(result);
2397
      end
2398
      else
2399
      begin
2400
        result := reg.ReadString('');
2401
      end;
2402
 
2403
      reg.CloseKey;
2404
    end;
2405
  finally
2406
    reg.Free;
2407
  end;
2408
end;
2409
 
2410
function RecyclerGetInfoTip: string;
2411
var
2412
  reg: TRegistry;
2413
begin
2414
  // Not available in some older versions of Windows
2415
 
2416
  reg := TRegistry.Create;
2417
  try
2418
    reg.RootKey := HKEY_CLASSES_ROOT;
2419
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
2420
    begin
2421
      result := reg.ReadString('InfoTip');
2422
      result := _DecodeReferenceString(result);
2423
 
2424
      reg.CloseKey;
2425
    end;
2426
  finally
2427
    reg.Free;
2428
  end;
2429
end;
2430
 
2431
function RecyclerGetIntroText: string;
2432
var
2433
  reg: TRegistry;
2434
begin
2435
  // Not available in some older versions of Windows
2436
 
2437
  reg := TRegistry.Create;
2438
  try
2439
    reg.RootKey := HKEY_CLASSES_ROOT;
2440
    if reg.OpenKeyReadOnly('CLSID\'+RECYCLER_CLSID) then
2441
    begin
2442
      result := reg.ReadString('IntroText');
2443
      result := _DecodeReferenceString(result);
2444
 
2445
      reg.CloseKey;
2446
    end;
2447
  finally
2448
    reg.Free;
2449
  end;
2450
end;
2451
 
2452
function RecyclerEmptyEventGetName: string;
2453
var
2454
  reg: TRegistry;
2455
begin
2456
  reg := TRegistry.Create;
2457
  try
2458
    reg.RootKey := HKEY_CURRENT_USER;
2459
    if reg.OpenKeyReadOnly('AppEvents\EventLabels\EmptyRecycleBin') then
2460
    begin
2461
      result := reg.ReadString('');
2462
      reg.CloseKey;
2463
    end;
2464
  finally
2465
    reg.Free;
2466
  end;
2467
end;
2468
 
2469
function RecyclerEmptyEventGetCurrentSound: string;
2470
begin
2471
  result := RecyclerEmptyEventGetSound('.Current');
2472
end;
2473
 
2474
function RecyclerEmptyEventGetDefaultSound: string;
2475
begin
2476
  result := RecyclerEmptyEventGetSound('.Default');
2477
end;
2478
 
2479
procedure RecyclerEmptyEventGetSoundCategories(AStringList: TStringList);
2480
var
2481
  reg: TRegistry;
2482
begin
2483
  reg := TRegistry.Create;
2484
  try
2485
    reg.RootKey := HKEY_CURRENT_USER;
2486
    if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
2487
    begin
2488
      reg.GetKeyNames(AStringList);
2489
      reg.CloseKey;
2490
    end;
2491
  finally
2492
    reg.Free;
2493
  end;
2494
end;
2495
 
2496
function RecyclerEmptyEventGetSound(ACategory: string): string;
2497
var
2498
  reg: TRegistry;
2499
resourcestring
2500
  LNG_SND_EVENT_CAT_ERROR = 'The category "%s" is not available for the notification event "%s".';
2501
begin
2502
  // Outputs an filename or empty string for no sound defined.
2503
 
2504
  reg := TRegistry.Create;
2505
  try
2506
    reg.RootKey := HKEY_CURRENT_USER;
2507
    if reg.OpenKeyReadOnly('AppEvents\Schemes\Apps\Explorer\EmptyRecycleBin') then
2508
    begin
2509
      if reg.OpenKeyReadOnly(ACategory) then
2510
      begin
2511
        result := reg.ReadString('');
2512
        reg.CloseKey;
2513
      end
2514
      else
2515
        raise EEventCategoryNotDefined.CreateFmt(LNG_SND_EVENT_CAT_ERROR, [ACategory, 'EmptyRecycleBin']);
2516
      reg.CloseKey;
2517
    end;
2518
  finally
2519
    reg.Free;
2520
  end;
2521
end;
2522
 
2523
function RecyclerGlobalGetPercentUsage: integer;
2524
var
2525
  reg: TRegistry;
2526
  dump: string;
2527
const
2528
  RES_DEFAULT = 10;
2529
begin
2530
  result := -1;
2531
 
2532
  reg := TRegistry.Create;
2533
  try
2534
    reg.RootKey := HKEY_LOCAL_MACHINE;
2535
 
2536
    if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
2537
    begin
2538
      if reg.ValueExists('Percent') then
2539
      begin
2540
        // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
2541
 
2542
        result := reg.ReadInteger('Percent');
2543
      end
2544
      else if reg.ValueExists('PurgeInfo') then
2545
      begin
2546
        // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
2547
 
2548
        dump := _registryReadDump(reg, 'PurgeInfo');
2549
        result := Ord(dump[63]);
2550
      end
2551
      else
2552
      begin
2553
        // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
2554
 
2555
        result := RES_DEFAULT; // Standardeinstellung bei Windows
2556
      end;
2557
 
2558
      reg.CloseKey;
2559
    end;
2560
  finally
2561
    reg.Free;
2562
  end;
2563
end;
2564
 
2565
function RecyclerSpecificGetPercentUsage(Drive: Char): integer;
2566
var
2567
  reg: TRegistry;
2568
  dump: string;
2569
const
2570
  RES_DEFAULT = 10;
2571
begin
2572
  result := -1;
2573
 
2574
  reg := TRegistry.Create;
2575
  try
2576
    reg.RootKey := HKEY_LOCAL_MACHINE;
2577
 
2578
    if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
2579
    begin
2580
      if reg.OpenKeyReadOnly(Drive) then
2581
      begin
2582
        if reg.ValueExists('Percent') then
2583
        begin
2584
          // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
2585
 
2586
          result := reg.ReadInteger('Percent');
2587
        end
2588
        else
2589
        begin
2590
          result := RES_DEFAULT;
2591
        end;
2592
        reg.CloseKey;
2593
      end
2594
      else
2595
      begin
2596
        if reg.ValueExists('PurgeInfo') then
2597
        begin
2598
          // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
2599
 
2600
          dump := _registryReadDump(reg, 'PurgeInfo');
2601
 
2602
          // NOT tested, only theoretical! My idea about the possible structure is:
2603
          // 0x08 = Drive A
2604
          // 0x0a = Drive B
2605
          // 0x0c = Drive C (validated)
2606
          // 0x0e = Drive D
2607
          // ...
2608
 
2609
          result := Ord(dump[9+_DriveNum(Drive)*2]);
2610
        end
2611
        else
2612
        begin
2613
          // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
2614
 
2615
          result := RES_DEFAULT; // Standardeinstellung bei Windows
2616
        end;
2617
      end;
2618
 
2619
      reg.CloseKey;
2620
    end;
2621
  finally
2622
    reg.Free;
2623
  end;
2624
end;
2625
 
2626
function RecyclerGetPercentUsageAutoDeterminate(Drive: Char): integer;
17 daniel-mar 2627
var
2628
  gpSetting: integer;
12 daniel-mar 2629
begin
17 daniel-mar 2630
  gpSetting := RecyclerGroupPolicyRecycleBinSize;
2631
  if gpSetting <> -1 then
2632
    result := gpSetting
2633
  else if RecyclerHasGlobalSettings then
12 daniel-mar 2634
    result := RecyclerGlobalGetPercentUsage
2635
  else
2636
    result := RecyclerSpecificGetPercentUsage(Drive);
2637
end;
2638
 
2639
function RecyclerGlobalIsNukeOnDelete: boolean;
2640
var
2641
  reg: TRegistry;
2642
  dump: string;
2643
const
2644
  RES_DEFAULT = false;
2645
begin
2646
  result := false;
2647
 
2648
  reg := TRegistry.Create;
2649
  try
2650
    reg.RootKey := HKEY_LOCAL_MACHINE;
2651
 
2652
    if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
2653
    begin
2654
      if reg.ValueExists('NukeOnDelete') then
2655
      begin
2656
        // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
2657
 
2658
        result := reg.ReadBool('NukeOnDelete');
2659
      end
2660
      else if reg.ValueExists('PurgeInfo') then
2661
      begin
2662
        // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
2663
 
2664
        // See comment at RecyclerSpecificIsNukeOnDelete()
2665
 
2666
        dump := _registryReadDump(reg, 'PurgeInfo');
30 daniel-mar 2667
        result := GetAnsiCharBit(dump[68], 3);
12 daniel-mar 2668
      end
2669
      else
2670
      begin
2671
        // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
2672
 
2673
        result := RES_DEFAULT; // Standardeinstellung bei Windows
2674
      end;
2675
 
2676
      reg.CloseKey;
2677
    end;
2678
  finally
2679
    reg.Free;
2680
  end;
2681
end;
2682
 
2683
function RecyclerSpecificIsNukeOnDelete(Drive: Char): boolean;
2684
var
2685
  reg: TRegistry;
2686
  dump: string;
2687
  d: Byte;
2688
const
2689
  RES_DEFAULT = false;
2690
begin
2691
  result := false;
2692
 
2693
  reg := TRegistry.Create;
2694
  try
2695
    reg.RootKey := HKEY_LOCAL_MACHINE;
2696
 
2697
    if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
2698
    begin
2699
      if reg.OpenKeyReadOnly(Drive) then
2700
      begin
2701
        if reg.ValueExists('NukeOnDelete') then
2702
        begin
2703
          // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
2704
 
2705
          result := reg.ReadBool('NukeOnDelete');
2706
        end;
2707
        reg.CloseKey;
2708
      end
2709
      else
2710
      begin
2711
        if reg.ValueExists('PurgeInfo') then
2712
        begin
2713
          // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
2714
 
2715
          dump := _registryReadDump(reg, 'PurgeInfo');
2716
 
2717
          // NOT tested, only theoretical! My idea about the possible structure is:
2718
          //
2719
          // Byte      0x40       0x41       0x42       0x43
2720
          // Bit       76543210   76543210   76543210   76543210
2721
          //           --------   --------   --------   --------
2722
          // Meaning   hgfedcba   ponmlkji   xwvutsrq   ????G?zy
2723
          //
2724
          // a..z = Drives
2725
          // G    = global settings
2726
          //
2727
          // Already validated:
2728
          // 0x64 = 04 (00000100)
2729
          // 0x67 = 08 (00001000)
2730
 
2731
          d := _DriveNum(Drive);
30 daniel-mar 2732
          result := GetAnsiCharBit(dump[65+(d div 7)], d mod 7);
12 daniel-mar 2733
        end
2734
        else
2735
        begin
2736
          // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
2737
 
2738
          result := RES_DEFAULT; // Standardeinstellung bei Windows
2739
        end;
2740
      end;
2741
 
2742
      reg.CloseKey;
2743
    end;
2744
  finally
2745
    reg.Free;
2746
  end;
2747
end;
2748
 
2749
function RecyclerIsNukeOnDeleteAutoDeterminate(Drive: Char): boolean;
2750
begin
17 daniel-mar 2751
  if RecyclerGroupPolicyNoRecycleFiles = gpEnabled then
2752
    result := true
2753
  else if RecyclerHasGlobalSettings then
12 daniel-mar 2754
    result := RecyclerGlobalIsNukeOnDelete
2755
  else
2756
    result := RecyclerSpecificIsNukeOnDelete(Drive);
2757
end;
2758
 
2759
function RecyclerHasGlobalSettings: boolean;
2760
var
2761
  reg: TRegistry;
2762
  dump: string;
2763
begin
2764
  result := false;
2765
 
2766
  reg := TRegistry.Create;
2767
  try
2768
    reg.RootKey := HKEY_LOCAL_MACHINE;
2769
 
2770
    if reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\BitBucket') then
2771
    begin
2772
      if reg.ValueExists('UseGlobalSettings') then
2773
      begin
2774
        // Windows 2000 - Informationen liegen aufgeschlüsselt in der Registry
2775
 
2776
        result := reg.ReadBool('UseGlobalSettings');
2777
      end
2778
      else if reg.ValueExists('PurgeInfo') then
2779
      begin
2780
        // Windows 95 - Verschlüsselte Informationen liegen in PurgeInfo
2781
        // TODO: Gibt es ein offizielles Dokument oder ein API, indem PurgeInfo
2782
        // offiziell entschlüsselbar ist?
2783
 
2784
        dump := _registryReadDump(reg, 'PurgeInfo');
2785
        if dump[5] = #$01 then
2786
          result := true
2787
        else if dump[5] = #$00 then
2788
          result := false
2789
        else
2790
          raise EUnknownState.Create(Format(LNG_UNEXPECTED_STATE, ['PurgeInfo']));
2791
      end
2792
      else
2793
      begin
2794
        // Windows 95 - Standardwerte sind gegeben, deswegen existiert kein PurgeInfo
2795
 
2796
        result := true; // Standardeinstellung bei Windows
2797
      end;
2798
 
2799
      reg.CloseKey;
2800
    end;
2801
  finally
2802
    reg.Free;
2803
  end;
2804
end;
2805
 
2806
function RecyclerIsEmpty: boolean;
2807
var
2808
  Drive: Char;
2809
begin
2810
  result := true;
2811
  for Drive := 'A' to 'Z' do
2812
  begin
24 daniel-mar 2813
    if RecyclerIsPossible(Drive) and not RecyclerIsEmpty(Drive) then
12 daniel-mar 2814
    begin
2815
      result := false;
2816
      exit;
2817
    end;
2818
  end;
2819
end;
2820
 
2821
function RecyclerIsEmpty(Drive: Char): boolean;
2822
begin
2823
  result := RecyclerGetAPIInfo(Drive).i64NumItems = 0;
2824
end;
2825
 
2826
function RecyclerGetNumItems: int64;
2827
var
2828
  Drive: Char;
2829
begin
2830
  result := 0;
2831
  for Drive := 'A' to 'Z' do
2832
  begin
24 daniel-mar 2833
    if RecyclerIsPossible(Drive) then
2834
    begin
2835
      result := result + RecyclerGetNumItems(Drive);
2836
    end;
12 daniel-mar 2837
  end;
2838
end;
2839
 
2840
function RecyclerGetNumItems(Drive: Char): int64;
2841
begin
2842
  result := RecyclerGetAPIInfo(Drive).i64NumItems;
2843
end;
2844
 
2845
function RecyclerGetSize: int64;
2846
var
2847
  Drive: Char;
2848
begin
2849
  result := 0;
2850
  for Drive := 'A' to 'Z' do
2851
  begin
24 daniel-mar 2852
    if RecyclerIsPossible(Drive) then
2853
    begin
2854
      result := result + RecyclerGetSize(Drive);
2855
    end;
12 daniel-mar 2856
  end;
2857
end;
2858
 
2859
function RecyclerGetSize(Drive: Char): int64;
2860
begin
2861
  result := RecyclerGetAPIInfo(Drive).i64Size;
2862
end;
2863
 
2864
function RecyclerGetAPIInfo(Drive: Char): TSHQueryRBInfo;
2865
begin
2866
  result := RecyclerGetAPIInfo(Drive + ':\');
2867
end;
2868
 
2869
const
2870
  C_SHQueryRecycleBin = 'SHQueryRecycleBinA';
2871
 
2872
type
2873
  TSHQueryRecycleBin = function(pszRootPath: LPCTSTR;
2874
    var pSHQueryRBInfo: TSHQueryRBInfo): HRESULT; stdcall;
2875
 
2876
function RecyclerGetAPIInfo(Path: String): TSHQueryRBInfo;
2877
var
2878
  PSHQueryRecycleBin: TSHQueryRecycleBin;
2879
  RBHandle: THandle;
2880
  res: HRESULT;
2881
begin
2882
  PSHQueryRecycleBin := nil;
2883
 
2884
  // Ref: http://www.delphipraxis.net/post1291.html
2885
 
2886
  RBHandle := LoadLibrary(shell32);
2887
  if(RBHandle <> 0) then
2888
  begin
2889
    PSHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
2890
    if(@PSHQueryRecycleBin = nil) then
2891
    begin
2892
      FreeLibrary(RBHandle);
2893
      RBHandle := 0;
2894
    end;
2895
  end;
2896
 
2897
  fillchar(result, SizeOf(TSHQueryRBInfo),0);
2898
  result.cbSize := SizeOf(TSHQueryRBInfo);
2899
 
2900
  if (RBHandle <> 0) and (Assigned(PSHQueryRecycleBin)) then
2901
  begin
2902
    res := PSHQueryRecycleBin(PChar(Path), result);
2903
    // if Succeeded(res) then
2904
    if res = S_OK then
2905
    begin
2906
      // Alles OK, unser result hat nun die gewünschten Daten.
2907
    end
2908
    else
24 daniel-mar 2909
    begin
2910
      // Since Windows Vista, SHQueryRecycleBin will fail with E_FAIL (80004005)
2911
      // if Path is a floppy or CD drive...
2912
      raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_ERROR_CODE, [C_SHQueryRecycleBin, Path, '0x'+IntToHex(res, 2*SizeOf(HRESULT))])]);
2913
    end;
12 daniel-mar 2914
  end
2915
  else
2916
    raise EAPICallError.CreateFmt(LNG_API_CALL_ERROR, [Format(LNG_NOT_CALLABLE, [C_SHQueryRecycleBin])]);
2917
 
2918
  if (RBHandle <> 0) then FreeLibrary(RBHandle);
2919
end;
2920
 
2921
function RecyclerGetCLSID: string;
2922
begin
2923
  result := RECYCLER_CLSID;
2924
end;
2925
 
17 daniel-mar 2926
// Windows 95 without Internet Explorer 4 has no SHQueryRecycleBinA.
12 daniel-mar 2927
function RecyclerQueryFunctionAvailable: boolean;
2928
var
2929
  RBHandle: THandle;
2930
  SHQueryRecycleBin: TSHQueryRecycleBin;
2931
begin
2932
  RBHandle := LoadLibrary(shell32);
2933
  if(RBHandle <> 0) then
2934
  begin
2935
    SHQueryRecycleBin := GetProcAddress(RBHandle, C_SHQueryRecycleBin);
2936
    if(@SHQueryRecycleBin = nil) then
2937
    begin
2938
      FreeLibrary(RBHandle);
2939
      RBHandle := 0;
2940
    end;
2941
  end;
2942
 
2943
  result := RBHandle <> 0;
2944
end;
2945
 
17 daniel-mar 2946
const
2947
  GroupPolicyAcceptHKLMTrick = true;
2948
 
2949
// TODO: In future also detect for other users
2950
// TODO: Also make a setter (inkl. Message to Windows Explorer?)
2951
function RecyclerGroupPolicyNoRecycleFiles: GPOLICYBOOL;
2952
var
2953
  reg: TRegistry;
2954
begin
2955
  result := gpUndefined;
2956
 
2957
  reg := TRegistry.Create;
2958
  try
2959
    // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
2960
    // even if gpedit.msc shows "Not configured"!
2961
    if GroupPolicyAcceptHKLMTrick then
2962
    begin
2963
      reg.RootKey := HKEY_LOCAL_MACHINE;
2964
      if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
2965
      begin
2966
        if reg.ValueExists('NoRecycleFiles') then
2967
        begin
2968
          if reg.ReadBool('NoRecycleFiles') then
2969
            result := gpEnabled
2970
          else
2971
            result := gpDisabled;
2972
          Exit;
2973
        end;
2974
        reg.CloseKey;
2975
      end;
2976
    end;
2977
 
2978
    reg.RootKey := HKEY_CURRENT_USER;
2979
    if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
2980
    begin
2981
      if reg.ValueExists('NoRecycleFiles') then
2982
      begin
2983
        if reg.ReadBool('NoRecycleFiles') then
2984
          result := gpEnabled
2985
        else
2986
          result := gpDisabled;
2987
      end;
2988
      reg.CloseKey;
2989
    end;
2990
  finally
2991
    reg.Free;
2992
  end;
2993
end;
2994
 
2995
// TODO: In future also detect for other users
2996
// TODO: Also make a setter (inkl. Message to Windows Explorer?)
2997
function RecyclerGroupPolicyConfirmFileDelete: GPOLICYBOOL;
2998
var
2999
  reg: TRegistry;
3000
begin
3001
  result := gpUndefined;
3002
  reg := TRegistry.Create;
3003
  try
3004
    // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
3005
    // even if gpedit.msc shows "Not configured"!
3006
    if GroupPolicyAcceptHKLMTrick then
3007
    begin
3008
      reg.RootKey := HKEY_LOCAL_MACHINE;
3009
      if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
3010
      begin
3011
        if reg.ValueExists('ConfirmFileDelete') then
3012
        begin
3013
          if reg.ReadBool('ConfirmFileDelete') then
3014
            result := gpEnabled
3015
          else
3016
            result := gpDisabled;
3017
          Exit;
3018
        end;
3019
        reg.CloseKey;
3020
      end;
3021
    end;
3022
 
3023
    reg.RootKey := HKEY_CURRENT_USER;
3024
    if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
3025
    begin
3026
      if reg.ValueExists('ConfirmFileDelete') then
3027
      begin
3028
        if reg.ReadBool('ConfirmFileDelete') then
3029
          result := gpEnabled
3030
        else
3031
          result := gpDisabled;
3032
      end;
3033
      reg.CloseKey;
3034
    end;
3035
  finally
3036
    reg.Free;
3037
  end;
3038
end;
3039
 
3040
 
3041
// TODO: In future also detect for other users
3042
// TODO: Also make a setter (inkl. Message to Windows Explorer?)
3043
function RecyclerGroupPolicyRecycleBinSize: integer;
3044
var
3045
  reg: TRegistry;
3046
begin
3047
  result := -1;
3048
  reg := TRegistry.Create;
3049
  try
3050
    // If a value is set in HKEY_LOCAL_MACHINE, it will be prefered,
3051
    // even if gpedit.msc shows "Not configured"!
3052
    if GroupPolicyAcceptHKLMTrick then
3053
    begin
3054
      reg.RootKey := HKEY_LOCAL_MACHINE;
3055
      if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
3056
      begin
3057
        if reg.ValueExists('RecycleBinSize') then
3058
        begin
3059
          result := reg.ReadInteger('RecycleBinSize');
3060
          Exit;
3061
        end;
3062
        reg.CloseKey;
3063
      end;
3064
    end;
3065
 
3066
    reg.RootKey := HKEY_CURRENT_USER;
3067
    if reg.OpenKeyReadOnly('Software\Microsoft\Windows\CurrentVersion\Policies\Explorer') then
3068
    begin
3069
      if reg.ValueExists('RecycleBinSize') then
18 daniel-mar 3070
      begin
17 daniel-mar 3071
        result := reg.ReadInteger('RecycleBinSize');
18 daniel-mar 3072
      end;
17 daniel-mar 3073
      reg.CloseKey;
3074
    end;
3075
  finally
3076
    reg.Free;
3077
  end;
3078
end;
3079
 
3080
function GPBoolToString(value: GPOLICYBOOL): String;
3081
begin
3082
  case value of
3083
    gpUndefined: result := 'Not configured';
3084
    gpEnabled: result := 'Enabled';
3085
    gpDisabled: result := 'Disabled';
3086
  end;
3087
end;
3088
 
22 daniel-mar 3089
function RecyclerIsPossible(Drive: Char): boolean;
3090
var
3091
  typ: Integer;
3092
begin
3093
  typ := GetDriveType(PChar(Drive + ':\'));
3094
  result := typ = DRIVE_FIXED;
3095
end;
3096
 
12 daniel-mar 3097
function RecyclerLibraryVersion: string;
3098
begin
27 daniel-mar 3099
  result := 'ViaThinkSoft Recycle Bin Unit [05 JUL 2010]';
12 daniel-mar 3100
end;
3101
 
3102
end.