Subversion Repositories delphiutils

Rev

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

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