Subversion Repositories recyclebinunit

Rev

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

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