Subversion Repositories recyclebinunit

Rev

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

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