Subversion Repositories indexer_suite

Rev

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

Rev 4 Rev 6
1
unit IndexCreatorForm;
1
unit IndexCreatorForm;
2
 
2
 
3
// TODO: vor einem fehler bitte vorher einen löschvorgang durchführen --> geht nicht?
3
// TODO: vor einem fehler bitte vorher einen löschvorgang durchführen --> geht nicht?
4
// TODO: berücksichtigen, wenn datei gesperrt. etc, fehler anschauen
4
// TODO: berücksichtigen, wenn datei gesperrt. etc, fehler anschauen
5
// TODO: warum sind in der db mehr einträge als dateien auf der festplatte sind?!
5
// TODO: warum sind in der db mehr einträge als dateien auf der festplatte sind?!
6
// TODO: Möglichkeit geben, Dateien und Verzeichnisse auszuschließen
6
// TODO: Möglichkeit geben, Dateien und Verzeichnisse auszuschließen
7
// TODO: should we include flags (readonly, invisible, compressed, encrypted)?
7
// TODO: should we include flags (readonly, invisible, compressed, encrypted)?
8
// TODO: search+replace tool, wenn man große verschiebungen vorgenommen hat
8
// TODO: search+replace tool, wenn man große verschiebungen vorgenommen hat
9
// update top (100000) files set filename = replace(filename, '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\', 'EHDD:\') where filename like '%\\?\%';
9
// update top (100000) files set filename = replace(filename, '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\', 'EHDD:\') where filename like '%\\?\%';
10
// TODO: anzeige, wie viele stunden der prozess schon läuft
10
// TODO: anzeige, wie viele stunden der prozess schon läuft
11
// TODO: multithreading
11
// TODO: multithreading
12
// TODO: diverse tools schreiben, die die datenbank nutzen, z.b. ein tool, das prüft, ob ein verzeichnis vollständig redundant ist
12
// TODO: diverse tools schreiben, die die datenbank nutzen, z.b. ein tool, das prüft, ob ein verzeichnis vollständig redundant ist
13
// TODO: Beim Lauf F:\nas\data wurden 1312 Fehler gefunden, aber nicht geloggt! ?! Eine exception im exception handler?!
13
// TODO: Beim Lauf F:\nas\data wurden 1312 Fehler gefunden, aber nicht geloggt! ?! Eine exception im exception handler?!
14
// => nochmal durchlaufen lassen
14
// => nochmal durchlaufen lassen
15
// TODO: "Laufwerk" EHDD: soll man auch eingeben dürfen (das ist z.b. wichtig, wenn man Querverknüpfung vom Explorer verwendet)
15
// TODO: "Laufwerk" EHDD: soll man auch eingeben dürfen (das ist z.b. wichtig, wenn man Querverknüpfung vom Explorer verwendet)
16
// TODO: validate modus auch ohne prüfsummencheck. nur gucken ob die dateien existieren
16
// TODO: validate modus auch ohne prüfsummencheck. nur gucken ob die dateien existieren
17
 
17
 
18
{$DEFINE VIATHINKSOFT}
18
{$DEFINE VIATHINKSOFT}
19
 
19
 
20
interface
20
interface
21
 
21
 
22
uses
22
uses
23
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
23
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
24
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, DB, ADODB, Menus;
24
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, DB, ADODB, Menus;
25
 
25
 
26
const
26
const
27
  modusUpdate = 0;
27
  modusUpdate = 0;
28
  modusRecreate = 1;
28
  modusRecreate = 1;
29
  modusValidation = 2;
29
  modusValidation = 2;
30
 
30
 
31
type
31
type
32
  TfrmIndexCreator = class(TForm)
32
  TfrmIndexCreator = class(TForm)
33
    Button1: TButton;
33
    Button1: TButton;
34
    Label1: TLabel;
34
    Label1: TLabel;
35
    LabeledEdit2: TLabeledEdit;
35
    LabeledEdit2: TLabeledEdit;
36
    Button2: TButton;
36
    Button2: TButton;
37
    Label2: TLabel;
37
    Label2: TLabel;
38
    Label3: TLabel;
38
    Label3: TLabel;
39
    Label4: TLabel;
39
    Label4: TLabel;
40
    Label5: TLabel;
40
    Label5: TLabel;
41
    Label6: TLabel;
41
    Label6: TLabel;
42
    Label7: TLabel;
42
    Label7: TLabel;
43
    PopupMenu1: TPopupMenu;
43
    PopupMenu1: TPopupMenu;
44
    Copyuniquepathtoclipboard1: TMenuItem;
44
    Copyuniquepathtoclipboard1: TMenuItem;
45
    Label8: TLabel;
45
    Label8: TLabel;
46
    Label9: TLabel;
46
    Label9: TLabel;
47
    Label10: TLabel;
47
    Label10: TLabel;
48
    Label11: TLabel;
48
    Label11: TLabel;
49
    Label12: TLabel;
49
    Label12: TLabel;
50
    Label13: TLabel;
50
    Label13: TLabel;
51
    Memo1: TMemo;
51
    Memo1: TMemo;
52
    Button4: TButton;
52
    Button4: TButton;
53
    Label14: TLabel;
53
    Label14: TLabel;
54
    cbNoDelete: TCheckBox;
54
    cbNoDelete: TCheckBox;
55
    Memo2: TMemo;
55
    Memo2: TMemo;
56
    cbVerboseLogs: TCheckBox;
56
    cbVerboseLogs: TCheckBox;
57
    cbSimulate: TCheckBox;
57
    cbSimulate: TCheckBox;
58
    rgModus: TRadioGroup;
58
    rgModus: TRadioGroup;
59
    procedure Button1Click(Sender: TObject);
59
    procedure Button1Click(Sender: TObject);
60
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
60
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
61
    procedure Button2Click(Sender: TObject);
61
    procedure Button2Click(Sender: TObject);
62
    procedure FormShow(Sender: TObject);
62
    procedure FormShow(Sender: TObject);
63
    procedure Copyuniquepathtoclipboard1Click(Sender: TObject);
63
    procedure Copyuniquepathtoclipboard1Click(Sender: TObject);
64
    procedure Button4Click(Sender: TObject);
64
    procedure Button4Click(Sender: TObject);
65
    procedure rgModusClick(Sender: TObject);
65
    procedure rgModusClick(Sender: TObject);
66
  private
66
  private
67
    StopRequest: boolean;
67
    StopRequest: boolean;
68
    sumsize: int64;
68
    sumsize: int64;
69
    sumfiles: int64;
69
    sumfiles: int64;
70
    sumfiles_new: int64;
70
    sumfiles_new: int64;
71
    sumfiles_updated: int64;
71
    sumfiles_updated: int64;
72
    sumfiles_error: int64;
72
    sumfiles_error: int64;
73
    sumfiles_deleted: int64;
73
    sumfiles_deleted: int64;
74
    sumfiles_integrityfail: int64;
74
    sumfiles_integrityfail: int64;
75
    function TableName: string;
75
    function TableName: string;
76
    function conn: TAdoConnection;
76
    function conn: TAdoConnection;
77
    procedure Rec(StartDir: string; const FileMask: string);
77
    procedure Rec(StartDir: string; const FileMask: string);
78
    procedure CheckFile(const originalFileName, uniqueFilename: string);
78
    procedure CheckFile(const originalFileName, uniqueFilename: string);
79
    procedure EnableDisableControls(enabled: boolean);
79
    procedure EnableDisableControls(enabled: boolean);
80
    procedure IndexDrive(initialdir: string);
80
    procedure IndexDrive(initialdir: string);
81
    procedure RedrawStats;
81
    procedure RedrawStats;
82
    procedure DeleteVanishedFiles(mask: string = '');
82
    procedure DeleteVanishedFiles(mask: string = '');
83
    class function DriveGuid(const Letter: char): string; static;
83
    class function DriveGuid(const Letter: char): string; static;
84
    class function uniqueFilename(const filename: string): string; static;
84
    class function uniqueFilename(const filename: string): string; static;
85
    class function VtsSpecial(const filename: string): string; static;
85
    class function VtsSpecial(const filename: string): string; static;
86
    procedure DeleteAllFiles(mask: string = '');
86
    procedure DeleteAllFiles(mask: string = '');
87
  end;
87
  end;
88
 
88
 
89
implementation
89
implementation
90
 
90
 
91
{$R *.dfm}
91
{$R *.dfm}
92
 
92
 
93
uses
93
uses
94
  FileCtrl, DateUtils, inifiles, IdHashMessageDigest, idHash, Math, Clipbrd,
94
  FileCtrl, DateUtils, inifiles, IdHashMessageDigest, idHash, Math, Clipbrd,
95
  StrUtils, AdoConnHelper, MainForm;
95
  StrUtils, AdoConnHelper, MainForm;
96
 
96
 
97
const
97
const
98
  Win32ImportSuffix = {$IFDEF Unicode}'W'{$ELSE}'A'{$ENDIF};
98
  Win32ImportSuffix = {$IFDEF Unicode}'W'{$ELSE}'A'{$ENDIF};
99
 
99
 
100
function GetVolumeNameForVolumeMountPointA(lpszVolumeMountPoint: PAnsiChar;
100
function GetVolumeNameForVolumeMountPointA(lpszVolumeMountPoint: PAnsiChar;
101
  lpszVolumeName: PAnsiChar; cchBufferLength: DWORD): BOOL; stdcall;
101
  lpszVolumeName: PAnsiChar; cchBufferLength: DWORD): BOOL; stdcall;
102
  external 'kernel32.dll';
102
  external 'kernel32.dll';
103
function GetVolumeNameForVolumeMountPointW(lpszVolumeMountPoint: PWideChar;
103
function GetVolumeNameForVolumeMountPointW(lpszVolumeMountPoint: PWideChar;
104
  lpszVolumeName: PWideChar; cchBufferLength: DWORD): BOOL; stdcall;
104
  lpszVolumeName: PWideChar; cchBufferLength: DWORD): BOOL; stdcall;
105
  external 'kernel32.dll';
105
  external 'kernel32.dll';
106
function GetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint: PChar;
106
function GetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint: PChar;
107
  lpszVolumeName: PChar; cchBufferLength: DWORD): BOOL; stdcall;
107
  lpszVolumeName: PChar; cchBufferLength: DWORD): BOOL; stdcall;
108
  external 'kernel32.dll' name 'GetVolumeNameForVolumeMountPoint' +
108
  external 'kernel32.dll' name 'GetVolumeNameForVolumeMountPoint' +
109
  Win32ImportSuffix;
109
  Win32ImportSuffix;
110
 
110
 
111
const
111
const
112
  ERROR_FIELD_SIZE = 200;
112
  ERROR_FIELD_SIZE = 200;
113
{$IFDEF VIATHINKSOFT}
113
{$IFDEF VIATHINKSOFT}
114
  GUID_EHDD_A = '\\?\Volume{31e044b1-28dc-11e6-9bae-d067e54bf736}\';
114
  GUID_EHDD_A = '\\?\Volume{31e044b1-28dc-11e6-9bae-d067e54bf736}\';
115
  GUID_EHDD_B = '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\';
115
  GUID_EHDD_B = '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\';
116
{$ENDIF}
116
{$ENDIF}
117
 
117
 
118
function MD5File(const filename: string): string;
118
function MD5File(const filename: string): string;
119
var
119
var
120
  IdMD5: TIdHashMessageDigest5;
120
  IdMD5: TIdHashMessageDigest5;
121
  FS: TFileStream;
121
  FS: TFileStream;
122
begin
122
begin
123
  IdMD5 := TIdHashMessageDigest5.Create;
123
  IdMD5 := TIdHashMessageDigest5.Create;
124
  FS := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite);
124
  FS := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite);
125
  try
125
  try
126
{$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed.
126
{$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed.
127
    Result := IdMD5.HashStreamAsHex(FS);
127
    Result := IdMD5.HashStreamAsHex(FS);
128
{$ELSE}
128
{$ELSE}
129
    Result := IdMD5.AsHex(IdMD5.HashValue(FS));
129
    Result := IdMD5.AsHex(IdMD5.HashValue(FS));
130
{$ENDIF}
130
{$ENDIF}
131
  finally
131
  finally
132
    FS.Free;
132
    FS.Free;
133
    IdMD5.Free;
133
    IdMD5.Free;
134
  end;
134
  end;
135
end;
135
end;
136
 
136
 
137
function FileMTime_UTC(const filename: string): TDateTime;
137
function FileMTime_UTC(const filename: string): TDateTime;
138
var
138
var
139
  fad: TWin32FileAttributeData;
139
  fad: TWin32FileAttributeData;
140
  systime: SYSTEMTIME;
140
  systime: SYSTEMTIME;
141
begin
141
begin
142
  if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then
142
  if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then
143
    RaiseLastOSError;
143
    RaiseLastOSError;
144
 
144
 
145
  FileTimeToSystemTime(fad.ftLastWriteTime, systime);
145
  FileTimeToSystemTime(fad.ftLastWriteTime, systime);
146
 
146
 
147
  Result := SystemTimeToDateTime(systime);
147
  Result := SystemTimeToDateTime(systime);
148
end;
148
end;
149
 
149
 
150
function FileCTime_UTC(const filename: string): TDateTime;
150
function FileCTime_UTC(const filename: string): TDateTime;
151
var
151
var
152
  fad: TWin32FileAttributeData;
152
  fad: TWin32FileAttributeData;
153
  systime: SYSTEMTIME;
153
  systime: SYSTEMTIME;
154
begin
154
begin
155
  if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then
155
  if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then
156
    RaiseLastOSError;
156
    RaiseLastOSError;
157
 
157
 
158
  FileTimeToSystemTime(fad.ftCreationTime, systime);
158
  FileTimeToSystemTime(fad.ftCreationTime, systime);
159
 
159
 
160
  Result := SystemTimeToDateTime(systime);
160
  Result := SystemTimeToDateTime(systime);
161
end;
161
end;
162
 
162
 
163
function UTCTimeToLocalTime(const aValue: TDateTime): TDateTime;
-
 
164
var
-
 
165
  lBias: Integer;
-
 
166
  lTZI: TTimeZoneInformation;
-
 
167
begin
-
 
168
  lBias := 0;
-
 
169
  case GetTimeZoneInformation(lTZI) of
-
 
170
    TIME_ZONE_ID_UNKNOWN:
-
 
171
      lBias := lTZI.Bias;
-
 
172
    TIME_ZONE_ID_DAYLIGHT:
-
 
173
      lBias := lTZI.Bias + lTZI.DaylightBias;
-
 
174
    TIME_ZONE_ID_STANDARD:
-
 
175
      lBias := lTZI.Bias + lTZI.StandardBias;
-
 
176
  end;
-
 
177
  // UTC = local time + bias
-
 
178
  // bias is in number of minutes, TDateTime is in days
-
 
179
  Result := aValue - (lBias / (24 * 60));
-
 
180
end;
-
 
181
 
-
 
182
function GetFileSize(const AFileName: String): int64;
163
function GetFileSize(const AFileName: String): int64;
183
var
164
var
184
  lFindData: TWin32FindData;
165
  lFindData: TWin32FindData;
185
  lHandle: Cardinal;
166
  lHandle: Cardinal;
186
begin
167
begin
187
  // https://www.delphipraxis.net/24331-dateigroesse-einer-beliebigen-datei-ermitteln.html
168
  // https://www.delphipraxis.net/24331-dateigroesse-einer-beliebigen-datei-ermitteln.html
188
  lHandle := FindFirstFile(PChar(AFileName), lFindData);
169
  lHandle := FindFirstFile(PChar(AFileName), lFindData);
189
  if (lHandle <> INVALID_HANDLE_VALUE) then
170
  if (lHandle <> INVALID_HANDLE_VALUE) then
190
  begin
171
  begin
191
    Result := lFindData.nFileSizeLow;
172
    Result := lFindData.nFileSizeLow;
192
    PCardinal(Cardinal(@Result) + SizeOf(Cardinal))^ := lFindData.nFileSizeHigh;
173
    PCardinal(Cardinal(@Result) + SizeOf(Cardinal))^ := lFindData.nFileSizeHigh;
193
    Windows.FindClose(lHandle);
174
    Windows.FindClose(lHandle);
194
  end
175
  end
195
  else
176
  else
196
    Result := 0;
177
    Result := 0;
197
end;
178
end;
198
 
179
 
199
function IntToStr2(i: int64): string;
180
function IntToStr2(i: int64): string;
200
begin
181
begin
201
  // https://www.delphipraxis.net/150464-integer-mit-tausender-trennzeichen-ausgeben.html
182
  // https://www.delphipraxis.net/150464-integer-mit-tausender-trennzeichen-ausgeben.html
202
  Result := Format('%.0n', [i / 1]);
183
  Result := Format('%.0n', [i / 1]);
203
end;
184
end;
204
 
185
 
205
function ConvertBytes(Bytes: int64): string;
186
function ConvertBytes(Bytes: int64): string;
206
const
187
const
207
  Description: Array [0 .. 8] of string = ('Bytes', 'KB', 'MB', 'GB', 'TB',
188
  Description: Array [0 .. 8] of string = ('Bytes', 'KB', 'MB', 'GB', 'TB',
208
    'PB', 'EB', 'ZB', 'YB');
189
    'PB', 'EB', 'ZB', 'YB');
209
var
190
var
210
  i: Integer;
191
  i: Integer;
211
begin
192
begin
212
  // https://stackoverflow.com/questions/30548940/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-delphi
193
  // https://stackoverflow.com/questions/30548940/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-delphi
213
  i := 0;
194
  i := 0;
214
 
195
 
215
  while Bytes > Power(1024, i + 1) do
196
  while Bytes > Power(1024, i + 1) do
216
    Inc(i);
197
    Inc(i);
217
 
198
 
218
  Result := FormatFloat('###0.##', Bytes / IntPower(1024, i)) + ' ' +
199
  Result := FormatFloat('###0.##', Bytes / IntPower(1024, i)) + ' ' +
219
    Description[i];
200
    Description[i];
220
end;
201
end;
221
 
202
 
222
var
203
var
223
  DriveGuidCache: TStringList = nil;
204
  DriveGuidCache: TStringList = nil;
224
 
205
 
225
class function TfrmIndexCreator.DriveGuid(const Letter: char): string;
206
class function TfrmIndexCreator.DriveGuid(const Letter: char): string;
226
var
207
var
227
  Buffer: array [0 .. 49] of char;
208
  Buffer: array [0 .. 49] of char;
228
begin
209
begin
229
  if not Assigned(DriveGuidCache) then
210
  if not Assigned(DriveGuidCache) then
230
    DriveGuidCache := TStringList.Create;
211
    DriveGuidCache := TStringList.Create;
231
 
212
 
232
  Result := DriveGuidCache.Values[Letter];
213
  Result := DriveGuidCache.Values[Letter];
233
  if Result = '' then
214
  if Result = '' then
234
  begin
215
  begin
235
    Win32Check(GetVolumeNameForVolumeMountPoint(PChar(Letter + ':\'), Buffer,
216
    Win32Check(GetVolumeNameForVolumeMountPoint(PChar(Letter + ':\'), Buffer,
236
      Length(Buffer)));
217
      Length(Buffer)));
237
    Result := Buffer;
218
    Result := Buffer;
238
    DriveGuidCache.Values[Letter] := Result;
219
    DriveGuidCache.Values[Letter] := Result;
239
  end;
220
  end;
240
end;
221
end;
241
 
222
 
242
class function TfrmIndexCreator.uniqueFilename(const filename: string): string;
223
class function TfrmIndexCreator.uniqueFilename(const filename: string): string;
243
var
224
var
244
  guid: string;
225
  guid: string;
245
begin
226
begin
246
  if Length(filename) < 2 then
227
  if Length(filename) < 2 then
247
    exit;
228
    exit;
248
  if filename[2] = ':' then
229
  if filename[2] = ':' then
249
  begin
230
  begin
250
    guid := DriveGuid(filename[1]);
231
    guid := DriveGuid(filename[1]);
251
 
232
 
252
    Result := guid + Copy(filename, 4, Length(filename) - 3);
233
    Result := guid + Copy(filename, 4, Length(filename) - 3);
253
 
234
 
254
    // result := LowerCase(result);
235
    // result := LowerCase(result);
255
  end
236
  end
256
  else
237
  else
257
    Result := filename; // z.B. UNC-Pfad
238
    Result := filename; // z.B. UNC-Pfad
258
end;
239
end;
259
 
240
 
260
class function TfrmIndexCreator.VtsSpecial(const filename: string): string;
241
class function TfrmIndexCreator.VtsSpecial(const filename: string): string;
261
begin
242
begin
262
  Result := filename;
243
  Result := filename;
263
{$IFDEF VIATHINKSOFT}
244
{$IFDEF VIATHINKSOFT}
264
  Result := StringReplace(Result, GUID_EHDD_A, 'EHDD:\', []);
245
  Result := StringReplace(Result, GUID_EHDD_A, 'EHDD:\', []);
265
  Result := StringReplace(Result, GUID_EHDD_B, 'EHDD:\', []);
246
  Result := StringReplace(Result, GUID_EHDD_B, 'EHDD:\', []);
266
{$ENDIF}
247
{$ENDIF}
267
end;
248
end;
268
 
249
 
-
 
250
function SpecialCompare(a, b: TDateTime): boolean; // true = same timestamp
-
 
251
begin
-
 
252
  if SecondsBetween(a,b) < 2 then exit(true); // equal
-
 
253
 
-
 
254
  if SecondsBetween(a,b) > 7200 then exit(false);
-
 
255
 
-
 
256
  // Minute and Second equal, and difference is < 2h: fair enough, seems to be a DST issue
-
 
257
  if copy(TimeToStr(a),4,5) = copy(TimeToStr(b),4,5) then exit(true);
-
 
258
 
-
 
259
  result := false;
-
 
260
end;
-
 
261
 
269
procedure TfrmIndexCreator.CheckFile(const originalFileName,
262
procedure TfrmIndexCreator.CheckFile(const originalFileName,
270
  uniqueFilename: string);
263
  uniqueFilename: string);
271
 
264
 
272
  function DateTimeToSQL(dt: TDateTime): string;
265
  function DateTimeToSQL(dt: TDateTime): string;
273
  begin
266
  begin
274
    if dt = -1 then
267
    if dt = -1 then
275
      Result := 'NULL'
268
      Result := 'NULL'
276
    else
269
    else
277
      Result := conn.SQLStringEscape(DateTimetoStr(dt));
270
      Result := conn.SQLStringEscape(DateTimetoStr(dt));
278
  end;
271
  end;
279
 
272
 
280
type
273
type
281
  TExistResult = (erDoesNotExist, erHadError, erChanged, erUnchanged);
274
  TExistResult = (erDoesNotExist, erHadError, erChanged, erUnchanged);
282
 
275
 
283
var
276
var
284
  lastCheckedMd5: string;
277
  lastCheckedMd5: string;
285
 
278
 
286
  function Exists(const filename: string; size: int64;
279
  function Exists(const filename: string; size: int64;
287
    const modified: TDateTime): TExistResult;
280
    const modified: TDateTime): TExistResult;
288
  var
281
  var
289
    q: TADODataSet;
282
    q: TADODataSet;
290
  begin
283
  begin
291
    q := conn.GetTable('select error, size, modified, md5hash from ' + TableName
284
    q := conn.GetTable('select error, size, modified, md5hash from ' + TableName
292
      + ' where filename = ' + conn.SQLStringEscape
285
      + ' where filename = ' + conn.SQLStringEscape
293
      (VtsSpecial(uniqueFilename)));
286
      (VtsSpecial(uniqueFilename)));
294
    try
287
    try
295
      if q.RecordCount = 0 then
288
      if q.RecordCount = 0 then
296
        Result := erDoesNotExist
289
        Result := erDoesNotExist
297
      else if not q.Fields[0].IsNull then
290
      else if not q.Fields[0].IsNull then
298
        Result := erHadError
291
        Result := erHadError
299
      else if (q.Fields[1].AsString <> IntToStr(size)) or
292
      else if (q.Fields[1].AsString <> IntToStr(size)) or // we are combining strings because of int64
300
      // we are combining strings because of int64
-
 
301
        (SecondsBetween(q.Fields[2].AsDateTime, UTCTimeToLocalTime(modified)
293
        not SpecialCompare(q.Fields[2].AsDateTime, modified) then
302
        ) > 2) then
-
 
303
      begin
294
      begin
304
        Result := erChanged
295
        Result := erChanged
305
      end
296
      end
306
      else
297
      else
307
        Result := erUnchanged;
298
        Result := erUnchanged;
308
      lastCheckedMd5 := q.Fields[3].AsString;
299
      lastCheckedMd5 := q.Fields[3].AsString;
309
    finally
300
    finally
310
      FreeAndNil(q);
301
      FreeAndNil(q);
311
    end;
302
    end;
312
  end;
303
  end;
313
 
304
 
314
var
305
var
315
  created, modified: TDateTime;
306
  created, modified: TDateTime;
316
  size: int64;
307
  size: int64;
317
  md5: string;
308
  md5: string;
318
begin
309
begin
319
  Label1.Caption := MinimizeName(originalFileName, Label1.Canvas, Label1.Width);
310
  Label1.Caption := MinimizeName(originalFileName, Label1.Canvas, Label1.Width);
320
  Application.ProcessMessages;
311
  Application.ProcessMessages;
321
 
312
 
322
  try
313
  try
323
    if FileExists(uniqueFilename) then
314
    if FileExists(uniqueFilename) then
324
      created := FileCTime_UTC(uniqueFilename)
315
      created := FileCTime_UTC(uniqueFilename)
325
    else
316
    else
326
      created := -1;
317
      created := -1;
327
 
318
 
328
    if FileExists(uniqueFilename) then
319
    if FileExists(uniqueFilename) then
329
      modified := FileMTime_UTC(uniqueFilename)
320
      modified := FileMTime_UTC(uniqueFilename)
330
    else
321
    else
331
      modified := -1;
322
      modified := -1;
332
 
323
 
333
    size := GetFileSize(uniqueFilename);
324
    size := GetFileSize(uniqueFilename);
334
    Inc(sumsize, size);
325
    Inc(sumsize, size);
335
    Inc(sumfiles);
326
    Inc(sumfiles);
336
 
327
 
337
    if rgModus.ItemIndex = modusRecreate then
328
    if rgModus.ItemIndex = modusRecreate then
338
    begin
329
    begin
339
      md5 := MD5File(uniqueFilename);
330
      md5 := MD5File(uniqueFilename);
340
      if not cbSimulate.Checked then
331
      if not cbSimulate.Checked then
341
      begin
332
      begin
342
        conn.ExecSQL('INSERT INTO ' + TableName +
333
        conn.ExecSQL('INSERT INTO ' + TableName +
343
          ' (filename, size, created, modified, md5hash, error) values (' +
334
          ' (filename, size, created, modified, md5hash, error) values (' +
344
          conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' +
335
          conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' +
345
          IntToStr(size) + ', ' + DateTimeToSQL(UTCTimeToLocalTime(created)) +
336
          IntToStr(size) + ', ' + DateTimeToSQL(created) +
346
          ', ' + DateTimeToSQL(UTCTimeToLocalTime(modified)) + ', ' +
337
          ', ' + DateTimeToSQL(modified) + ', ' +
347
          conn.SQLStringEscape(LowerCase(md5)) + ', NULL);');
338
          conn.SQLStringEscape(LowerCase(md5)) + ', NULL);');
348
      end;
339
      end;
349
      if cbVerboseLogs.Checked then
340
      if cbVerboseLogs.Checked then
350
        Memo2.Lines.Add('New: ' + uniqueFilename);
341
        Memo2.Lines.Add('New: ' + uniqueFilename);
351
      Inc(sumfiles_new);
342
      Inc(sumfiles_new);
352
    end
343
    end
353
    else
344
    else
354
    begin
345
    begin
355
      case Exists(uniqueFilename, size, modified) of
346
      case Exists(uniqueFilename, size, modified) of
356
        erDoesNotExist: // File does not exist or has a different hash
347
        erDoesNotExist: // File does not exist or has a different hash
357
          begin
348
          begin
358
            if rgModus.ItemIndex <> modusValidation then
349
            if rgModus.ItemIndex <> modusValidation then
359
              md5 := MD5File(uniqueFilename);
350
              md5 := MD5File(uniqueFilename);
360
            if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
351
            if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
361
            then
352
            then
362
            begin
353
            begin
363
              conn.ExecSQL('INSERT INTO ' + TableName +
354
              conn.ExecSQL('INSERT INTO ' + TableName +
364
                ' (filename, size, created, modified, md5hash, error) values ('
355
                ' (filename, size, created, modified, md5hash, error) values ('
365
                + conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' +
356
                + conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' +
366
                IntToStr(size) + ', ' +
357
                IntToStr(size) + ', ' +
367
                DateTimeToSQL(UTCTimeToLocalTime(created)) + ', ' +
358
                DateTimeToSQL(created) + ', ' +
368
                DateTimeToSQL(UTCTimeToLocalTime(modified)) + ', ' +
359
                DateTimeToSQL(modified) + ', ' +
369
                conn.SQLStringEscape(LowerCase(md5)) + ', NULL);');
360
                conn.SQLStringEscape(LowerCase(md5)) + ', NULL);');
370
            end;
361
            end;
371
            if cbVerboseLogs.Checked then
362
            if cbVerboseLogs.Checked then
372
              Memo2.Lines.Add('New: ' + uniqueFilename);
363
              Memo2.Lines.Add('New: ' + uniqueFilename);
373
            Inc(sumfiles_new);
364
            Inc(sumfiles_new);
374
          end;
365
          end;
375
        erHadError, erChanged:
366
        erHadError, erChanged:
376
          begin
367
          begin
377
            if rgModus.ItemIndex <> modusValidation then
368
            if rgModus.ItemIndex <> modusValidation then
378
              md5 := MD5File(uniqueFilename);
369
              md5 := MD5File(uniqueFilename);
379
            if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
370
            if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
380
            then
371
            then
381
            begin
372
            begin
382
              conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' +
373
              conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' +
383
                IntToStr(size) + ', created = ' +
374
                IntToStr(size) + ', created = ' +
384
                DateTimeToSQL(UTCTimeToLocalTime(created)) + ', modified = ' +
375
                DateTimeToSQL(created) + ', modified = ' +
385
                DateTimeToSQL(UTCTimeToLocalTime(modified)) + ', md5hash = ' +
376
                DateTimeToSQL(modified) + ', md5hash = ' +
386
                conn.SQLStringEscape(LowerCase(md5)) +
377
                conn.SQLStringEscape(LowerCase(md5)) +
387
                ', error = NULL WHERE filename = ' + conn.SQLStringEscape
378
                ', error = NULL WHERE filename = ' + conn.SQLStringEscape
388
                (VtsSpecial(uniqueFilename)) + ';');
379
                (VtsSpecial(uniqueFilename)) + ';');
389
            end;
380
            end;
390
            if cbVerboseLogs.Checked then
381
            if cbVerboseLogs.Checked then
391
              Memo2.Lines.Add('Updated: ' + uniqueFilename);
382
              Memo2.Lines.Add('Updated: ' + uniqueFilename);
392
            Inc(sumfiles_updated);
383
            Inc(sumfiles_updated);
393
          end;
384
          end;
394
        erUnchanged: // Date/Time+Size has not changed
385
        erUnchanged: // Date/Time+Size has not changed
395
          begin
386
          begin
-
 
387
            {$REGION 'Update it to correct wrong UTC/DST datasets...'}
-
 
388
            conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' +
-
 
389
              IntToStr(size) + ', created = ' +
-
 
390
              DateTimeToSQL(created) + ', modified = ' +
-
 
391
              DateTimeToSQL(modified) +
-
 
392
              ', error = NULL WHERE filename = ' + conn.SQLStringEscape
-
 
393
              (VtsSpecial(uniqueFilename)) + ';');
-
 
394
            {$ENDREGION}
-
 
395
 
396
            if rgModus.ItemIndex = modusValidation then
396
            if rgModus.ItemIndex = modusValidation then
397
            begin
397
            begin
398
              md5 := MD5File(uniqueFilename);
398
              md5 := MD5File(uniqueFilename);
399
              if not SameText(md5, lastCheckedMd5) then
399
              if not SameText(md5, lastCheckedMd5) then
400
              begin
400
              begin
401
                Memo2.Lines.Add
401
                Memo2.Lines.Add
402
                  ('!!! HASH HAS CHANGED WHILE DATETIME+SIZE IS THE SAME: ' +
402
                  ('!!! HASH HAS CHANGED WHILE DATETIME+SIZE IS THE SAME: ' +
403
                  uniqueFilename + ' (' + lastCheckedMd5 + ' became ' +
403
                  uniqueFilename + ' (' + lastCheckedMd5 + ' became ' +
404
                  md5 + ')');
404
                  md5 + ')');
405
                Memo2.Color := clRed;
405
                Memo2.Color := clRed;
406
                Inc(sumfiles_integrityfail);
406
                Inc(sumfiles_integrityfail);
407
              end;
407
              end;
408
            end;
408
            end;
409
          end;
409
          end;
410
      end;
410
      end;
411
    end;
411
    end;
412
  except
412
  except
413
    on E: Exception do
413
    on E: Exception do
414
    begin
414
    begin
415
      if E is EAbort then
415
      if E is EAbort then
416
        Abort;
416
        Abort;
417
      // if AdoConnection1.InTransaction then AdoConnection1.RollbackTrans;
417
      // if AdoConnection1.InTransaction then AdoConnection1.RollbackTrans;
418
      // AdoConnection1.BeginTrans;
418
      // AdoConnection1.BeginTrans;
419
      try
419
      try
420
        if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
420
        if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
421
        then
421
        then
422
        begin
422
        begin
423
          conn.ExecSQL('DELETE FROM ' + TableName + ' WHERE filename = ' +
423
          conn.ExecSQL('DELETE FROM ' + TableName + ' WHERE filename = ' +
424
            conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ';');
424
            conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ';');
425
          conn.ExecSQL('INSERT INTO ' + TableName +
425
          conn.ExecSQL('INSERT INTO ' + TableName +
426
            ' (filename, size, created, modified, md5hash, error) values (' +
426
            ' (filename, size, created, modified, md5hash, error) values (' +
427
            conn.SQLStringEscape(VtsSpecial(uniqueFilename)) +
427
            conn.SQLStringEscape(VtsSpecial(uniqueFilename)) +
428
            ', NULL, NULL, NULL, NULL, ' + conn.SQLStringEscape(Copy(E.Message,
428
            ', NULL, NULL, NULL, NULL, ' + conn.SQLStringEscape(Copy(E.Message,
429
            1, ERROR_FIELD_SIZE)) + ');');
429
            1, ERROR_FIELD_SIZE)) + ');');
430
          Memo2.Lines.Add('Error (logged): ' + E.Message + ' at file ' +
430
          Memo2.Lines.Add('Error (logged): ' + E.Message + ' at file ' +
431
            VtsSpecial(uniqueFilename));
431
            VtsSpecial(uniqueFilename));
432
        end
432
        end
433
        else
433
        else
434
        begin
434
        begin
435
          Memo2.Lines.Add('Error: ' + E.Message + ' at file ' +
435
          Memo2.Lines.Add('Error: ' + E.Message + ' at file ' +
436
            VtsSpecial(uniqueFilename));
436
            VtsSpecial(uniqueFilename));
437
        end;
437
        end;
438
        // AdoConnection1.CommitTrans;
438
        // AdoConnection1.CommitTrans;
439
      except
439
      except
440
        // AdoConnection1.RollbackTrans;
440
        // AdoConnection1.RollbackTrans;
441
        Memo2.Lines.Add('Cannot write error into file database! ' + E.Message +
441
        Memo2.Lines.Add('Cannot write error into file database! ' + E.Message +
442
          ' at file ' + VtsSpecial(uniqueFilename));
442
          ' at file ' + VtsSpecial(uniqueFilename));
443
      end;
443
      end;
444
      Inc(sumfiles_error);
444
      Inc(sumfiles_error);
445
    end;
445
    end;
446
  end;
446
  end;
447
 
447
 
448
  RedrawStats;
448
  RedrawStats;
449
  Application.ProcessMessages;
449
  Application.ProcessMessages;
450
end;
450
end;
451
 
451
 
452
function TfrmIndexCreator.conn: TAdoConnection;
452
function TfrmIndexCreator.conn: TAdoConnection;
453
begin
453
begin
454
  Result := frmMain.AdoConnection1;
454
  Result := frmMain.AdoConnection1;
455
end;
455
end;
456
 
456
 
457
procedure TfrmIndexCreator.RedrawStats;
457
procedure TfrmIndexCreator.RedrawStats;
458
begin
458
begin
459
  Label5.Caption := ConvertBytes(sumsize);
459
  Label5.Caption := ConvertBytes(sumsize);
460
  Label6.Caption := IntToStr2(sumfiles);
460
  Label6.Caption := IntToStr2(sumfiles);
461
  Label7.Caption := IntToStr2(sumfiles_new);
461
  Label7.Caption := IntToStr2(sumfiles_new);
462
  Label9.Caption := IntToStr2(sumfiles_updated);
462
  Label9.Caption := IntToStr2(sumfiles_updated);
463
  Label11.Caption := IntToStr2(sumfiles_error);
463
  Label11.Caption := IntToStr2(sumfiles_error);
464
  Label12.Caption := IntToStr2(sumfiles_deleted);
464
  Label12.Caption := IntToStr2(sumfiles_deleted);
465
  // LabelXX.Caption := IntToStr2(sumfiles_integrityfail);
465
  // LabelXX.Caption := IntToStr2(sumfiles_integrityfail);
466
end;
466
end;
467
 
467
 
468
procedure TfrmIndexCreator.Copyuniquepathtoclipboard1Click(Sender: TObject);
468
procedure TfrmIndexCreator.Copyuniquepathtoclipboard1Click(Sender: TObject);
469
var
469
var
470
  s: string;
470
  s: string;
471
begin
471
begin
472
  s := uniqueFilename(LabeledEdit2.Text);
472
  s := uniqueFilename(LabeledEdit2.Text);
473
  Clipboard.AsText := s;
473
  Clipboard.AsText := s;
474
{$IFDEF VIATHINKSOFT}
474
{$IFDEF VIATHINKSOFT}
475
  if VtsSpecial(s) <> s then
475
  if VtsSpecial(s) <> s then
476
  begin
476
  begin
477
    s := s + #13#10 + VtsSpecial(s);
477
    s := s + #13#10 + VtsSpecial(s);
478
  end;
478
  end;
479
{$ENDIF}
479
{$ENDIF}
480
  ShowMessageFmt('Copied to clipboard:' + #13#10#13#10 + '%s', [s]);
480
  ShowMessageFmt('Copied to clipboard:' + #13#10#13#10 + '%s', [s]);
481
end;
481
end;
482
 
482
 
483
procedure TfrmIndexCreator.rgModusClick(Sender: TObject);
483
procedure TfrmIndexCreator.rgModusClick(Sender: TObject);
484
begin
484
begin
485
  cbSimulate.enabled := rgModus.ItemIndex <> modusValidation;
485
  cbSimulate.enabled := rgModus.ItemIndex <> modusValidation;
486
  cbNoDelete.enabled := rgModus.ItemIndex <> modusValidation;
486
  cbNoDelete.enabled := rgModus.ItemIndex <> modusValidation;
487
end;
487
end;
488
 
488
 
489
function TfrmIndexCreator.TableName: string;
489
function TfrmIndexCreator.TableName: string;
490
begin
490
begin
491
  Result := frmMain.TableName;
491
  Result := frmMain.TableName;
492
end;
492
end;
493
 
493
 
494
procedure TfrmIndexCreator.Rec(StartDir: string; const FileMask: string);
494
procedure TfrmIndexCreator.Rec(StartDir: string; const FileMask: string);
495
var
495
var
496
  SR: TSearchRec;
496
  SR: TSearchRec;
497
  DirList: TStrings;
497
  DirList: TStrings;
498
  IsFound: boolean;
498
  IsFound: boolean;
499
  i: Integer;
499
  i: Integer;
500
  UniqueStartDir: string;
500
  UniqueStartDir: string;
501
begin
501
begin
502
  StartDir := IncludeTrailingPathDelimiter(StartDir);
502
  StartDir := IncludeTrailingPathDelimiter(StartDir);
503
 
503
 
504
  i := 0;
504
  i := 0;
505
  conn.BeginTrans;
505
  conn.BeginTrans;
506
  IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0;
506
  IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0;
507
  try
507
  try
508
    while IsFound do
508
    while IsFound do
509
    begin
509
    begin
510
      Inc(i);
510
      Inc(i);
511
      if i mod 1000 = 0 then // Only for performance
511
      if i mod 1000 = 0 then // Only for performance
512
      begin
512
      begin
513
        conn.CommitTrans;
513
        conn.CommitTrans;
514
        conn.BeginTrans;
514
        conn.BeginTrans;
515
      end;
515
      end;
516
      Application.ProcessMessages;
516
      Application.ProcessMessages;
517
      if Application.Terminated or StopRequest then
517
      if Application.Terminated or StopRequest then
518
        Abort;
518
        Abort;
519
 
519
 
520
      if UniqueStartDir = '' then
520
      if UniqueStartDir = '' then
521
        UniqueStartDir := uniqueFilename(StartDir);
521
        UniqueStartDir := uniqueFilename(StartDir);
522
      CheckFile(StartDir + SR.Name, UniqueStartDir + SR.Name);
522
      CheckFile(StartDir + SR.Name, UniqueStartDir + SR.Name);
523
      IsFound := FindNext(SR) = 0;
523
      IsFound := FindNext(SR) = 0;
524
    end;
524
    end;
525
  finally
525
  finally
526
    FindClose(SR);
526
    FindClose(SR);
527
    conn.CommitTrans;
527
    conn.CommitTrans;
528
  end;
528
  end;
529
 
529
 
530
  // Build a list of subdirectories
530
  // Build a list of subdirectories
531
  DirList := TStringList.Create;
531
  DirList := TStringList.Create;
532
  try
532
  try
533
    IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0;
533
    IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0;
534
    try
534
    try
535
      while IsFound do
535
      while IsFound do
536
      begin
536
      begin
537
        if (SR.Name <> '.') and (SR.Name <> '..') then
537
        if (SR.Name <> '.') and (SR.Name <> '..') then
538
        begin
538
        begin
539
          Application.ProcessMessages;
539
          Application.ProcessMessages;
540
          if Application.Terminated or StopRequest then
540
          if Application.Terminated or StopRequest then
541
            Abort;
541
            Abort;
542
 
542
 
543
          DirList.Add(StartDir + SR.Name);
543
          DirList.Add(StartDir + SR.Name);
544
        end;
544
        end;
545
        IsFound := FindNext(SR) = 0;
545
        IsFound := FindNext(SR) = 0;
546
      end;
546
      end;
547
    finally
547
    finally
548
      FindClose(SR);
548
      FindClose(SR);
549
    end;
549
    end;
550
 
550
 
551
    // Scan the list of subdirectories
551
    // Scan the list of subdirectories
552
    for i := 0 to DirList.Count - 1 do
552
    for i := 0 to DirList.Count - 1 do
553
    begin
553
    begin
554
      try
554
      try
555
        Rec(DirList[i], FileMask);
555
        Rec(DirList[i], FileMask);
556
      except
556
      except
557
        on E: Exception do
557
        on E: Exception do
558
        begin
558
        begin
559
          if E is EAbort then
559
          if E is EAbort then
560
            Abort;
560
            Abort;
561
          Memo2.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' +
561
          Memo2.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' +
562
            E.Message);
562
            E.Message);
563
        end;
563
        end;
564
      end;
564
      end;
565
    end;
565
    end;
566
  finally
566
  finally
567
    DirList.Free;
567
    DirList.Free;
568
  end;
568
  end;
569
end;
569
end;
570
 
570
 
571
procedure TfrmIndexCreator.DeleteAllFiles(mask: string = '');
571
procedure TfrmIndexCreator.DeleteAllFiles(mask: string = '');
572
begin
572
begin
573
  sumfiles_deleted := conn.GetScalar('select count(*) as cnt from ' + TableName
573
  sumfiles_deleted := conn.GetScalar('select count(*) as cnt from ' + TableName
574
    + ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask)));
574
    + ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask)));
575
  RedrawStats;
575
  RedrawStats;
576
 
576
 
577
  if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) then
577
  if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) then
578
  begin
578
  begin
579
    if (mask = '') or (mask = '%') then
579
    if (mask = '') or (mask = '%') then
580
      conn.ExecSQL('delete from ' + TableName)
580
      conn.ExecSQL('delete from ' + TableName)
581
    else
581
    else
582
      conn.ExecSQL('delete from ' + TableName + ' where filename like ' +
582
      conn.ExecSQL('delete from ' + TableName + ' where filename like ' +
583
        conn.SQLStringEscape(VtsSpecial(mask)));
583
        conn.SQLStringEscape(VtsSpecial(mask)));
584
  end;
584
  end;
585
end;
585
end;
586
 
586
 
587
procedure TfrmIndexCreator.DeleteVanishedFiles(mask: string = '');
587
procedure TfrmIndexCreator.DeleteVanishedFiles(mask: string = '');
588
 
588
 
589
{$IFDEF VIATHINKSOFT}
589
{$IFDEF VIATHINKSOFT}
590
var
590
var
591
  cacheAconnected: boolean;
591
  cacheAconnected: boolean;
592
  cacheBconnected: boolean;
592
  cacheBconnected: boolean;
593
{$ENDIF}
593
{$ENDIF}
594
  function AllowFileCheck(AFileName: string): boolean;
594
  function AllowFileCheck(AFileName: string): boolean;
595
  var
595
  var
596
    guid: string;
596
    guid: string;
597
  begin
597
  begin
598
    Result := false;
598
    Result := false;
599
{$IFDEF VIATHINKSOFT}
599
{$IFDEF VIATHINKSOFT}
600
    if StartsText('EHDD:\', AFileName) then
600
    if StartsText('EHDD:\', AFileName) then
601
    begin
601
    begin
602
      if not cacheAconnected and SysUtils.DirectoryExists(GUID_EHDD_A) then
602
      if not cacheAconnected and SysUtils.DirectoryExists(GUID_EHDD_A) then
603
      begin
603
      begin
604
        cacheAconnected := true;
604
        cacheAconnected := true;
605
      end;
605
      end;
606
      if not cacheBconnected and SysUtils.DirectoryExists(GUID_EHDD_B) then
606
      if not cacheBconnected and SysUtils.DirectoryExists(GUID_EHDD_B) then
607
      begin
607
      begin
608
        cacheBconnected := true;
608
        cacheBconnected := true;
609
      end;
609
      end;
610
      Result := cacheAconnected or cacheBconnected;
610
      Result := cacheAconnected or cacheBconnected;
611
    end
611
    end
612
    else
612
    else
613
{$ENDIF}
613
{$ENDIF}
614
      if StartsText('\\?\Volume', AFileName) then
614
      if StartsText('\\?\Volume', AFileName) then
615
      begin
615
      begin
616
        guid := Copy(AFileName, 1, 49);
616
        guid := Copy(AFileName, 1, 49);
617
        if EndsText('\', guid) then // should always happen
617
        if EndsText('\', guid) then // should always happen
618
        begin
618
        begin
619
          // TODO: cache this result somehow, so that DirectoryExists() does not need to be called all the time
619
          // TODO: cache this result somehow, so that DirectoryExists() does not need to be called all the time
620
          if SysUtils.DirectoryExists(guid) then // is drive connected/existing?
620
          if SysUtils.DirectoryExists(guid) then // is drive connected/existing?
621
          begin
621
          begin
622
            Result := true;
622
            Result := true;
623
          end;
623
          end;
624
        end;
624
        end;
625
      end
625
      end
626
      else
626
      else
627
      begin
627
      begin
628
        // TODO: Einen Code für Netzlaufwerke machen: Wir dürfen nur Dateien löschen,
628
        // TODO: Einen Code für Netzlaufwerke machen: Wir dürfen nur Dateien löschen,
629
        // wenn das Netzlaufwerk wirklich da ist.
629
        // wenn das Netzlaufwerk wirklich da ist.
630
      end;
630
      end;
631
  end;
631
  end;
632
 
632
 
633
  function FileDoesExist(AFileName: string): boolean;
633
  function FileDoesExist(AFileName: string): boolean;
634
  begin
634
  begin
635
{$IFDEF VIATHINKSOFT}
635
{$IFDEF VIATHINKSOFT}
636
    if StartsText('EHDD:\', AFileName) then
636
    if StartsText('EHDD:\', AFileName) then
637
    begin
637
    begin
638
      // Attention: AllowFileCheck must be called to initialize cacheAconnected and cacheBconnected
638
      // Attention: AllowFileCheck must be called to initialize cacheAconnected and cacheBconnected
639
 
639
 
640
      if cacheAconnected and FileExists(StringReplace(AFileName, 'EHDD:\',
640
      if cacheAconnected and FileExists(StringReplace(AFileName, 'EHDD:\',
641
        GUID_EHDD_A, [])) then
641
        GUID_EHDD_A, [])) then
642
        exit(true);
642
        exit(true);
643
 
643
 
644
      if cacheBconnected and FileExists(StringReplace(AFileName, 'EHDD:\',
644
      if cacheBconnected and FileExists(StringReplace(AFileName, 'EHDD:\',
645
        GUID_EHDD_B, [])) then
645
        GUID_EHDD_B, [])) then
646
        exit(true);
646
        exit(true);
647
 
647
 
648
      exit(false);
648
      exit(false);
649
    end;
649
    end;
650
{$ENDIF}
650
{$ENDIF}
651
    exit(FileExists(AFileName));
651
    exit(FileExists(AFileName));
652
  end;
652
  end;
653
 
653
 
654
var
654
var
655
  filename: string;
655
  filename: string;
656
  q: TADODataSet;
656
  q: TADODataSet;
657
  fFileName: TField;
657
  fFileName: TField;
658
  i: int64;
658
  i: int64;
659
begin
659
begin
660
  if mask <> '' then
660
  if mask <> '' then
661
    q := conn.GetTable('select filename from ' + TableName +
661
    q := conn.GetTable('select filename from ' + TableName +
662
      ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask)))
662
      ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask)))
663
  else
663
  else
664
    q := conn.GetTable('select filename from ' + TableName);
664
    q := conn.GetTable('select filename from ' + TableName);
665
  try
665
  try
666
    i := 0;
666
    i := 0;
667
    fFileName := q.FieldByName('filename');
667
    fFileName := q.FieldByName('filename');
668
    while not q.Eof do
668
    while not q.Eof do
669
    begin
669
    begin
670
      filename := fFileName.AsString;
670
      filename := fFileName.AsString;
671
 
671
 
672
      if AllowFileCheck(filename) and not FileDoesExist(filename) then
672
      if AllowFileCheck(filename) and not FileDoesExist(filename) then
673
      begin
673
      begin
674
        if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
674
        if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation)
675
        then
675
        then
676
        begin
676
        begin
677
          conn.ExecSQL('delete from ' + TableName + ' where filename = ' +
677
          conn.ExecSQL('delete from ' + TableName + ' where filename = ' +
678
            conn.SQLStringEscape(filename));
678
            conn.SQLStringEscape(filename));
679
        end;
679
        end;
680
        Inc(sumfiles_deleted);
680
        Inc(sumfiles_deleted);
681
        if cbVerboseLogs.Checked then
681
        if cbVerboseLogs.Checked then
682
          Memo2.Lines.Add('Deleted: ' + filename);
682
          Memo2.Lines.Add('Deleted: ' + filename);
683
        RedrawStats;
683
        RedrawStats;
684
      end;
684
      end;
685
 
685
 
686
      Inc(i);
686
      Inc(i);
687
      if i mod 100 = 0 then
687
      if i mod 100 = 0 then
688
      begin
688
      begin
689
        Label1.Caption := MinimizeName(filename, Label1.Canvas, Label1.Width);
689
        Label1.Caption := MinimizeName(filename, Label1.Canvas, Label1.Width);
690
        Application.ProcessMessages;
690
        Application.ProcessMessages;
691
        if Application.Terminated or StopRequest then
691
        if Application.Terminated or StopRequest then
692
          Abort;
692
          Abort;
693
      end;
693
      end;
694
 
694
 
695
      q.Next;
695
      q.Next;
696
    end;
696
    end;
697
  finally
697
  finally
698
    FreeAndNil(q);
698
    FreeAndNil(q);
699
  end;
699
  end;
700
end;
700
end;
701
 
701
 
702
procedure TfrmIndexCreator.IndexDrive(initialdir: string);
702
procedure TfrmIndexCreator.IndexDrive(initialdir: string);
703
begin
703
begin
704
  if not cbNoDelete.Checked and not cbSimulate.Checked and
704
  if not cbNoDelete.Checked and not cbSimulate.Checked and
705
    (rgModus.ItemIndex <> modusValidation) then
705
    (rgModus.ItemIndex <> modusValidation) then
706
  begin
706
  begin
707
    if rgModus.ItemIndex = modusRecreate then
707
    if rgModus.ItemIndex = modusRecreate then
708
    begin
708
    begin
709
      DeleteAllFiles(uniqueFilename(IncludeTrailingPathDelimiter
709
      DeleteAllFiles(uniqueFilename(IncludeTrailingPathDelimiter
710
        (initialdir)) + '%');
710
        (initialdir)) + '%');
711
    end
711
    end
712
    else
712
    else
713
    begin
713
    begin
714
      DeleteVanishedFiles
714
      DeleteVanishedFiles
715
        (uniqueFilename(IncludeTrailingPathDelimiter(initialdir)) + '%');
715
        (uniqueFilename(IncludeTrailingPathDelimiter(initialdir)) + '%');
716
    end;
716
    end;
717
  end;
717
  end;
718
 
718
 
719
  Rec(IncludeTrailingPathDelimiter(initialdir), '*');
719
  Rec(IncludeTrailingPathDelimiter(initialdir), '*');
720
end;
720
end;
721
 
721
 
722
procedure TfrmIndexCreator.Button1Click(Sender: TObject);
722
procedure TfrmIndexCreator.Button1Click(Sender: TObject);
723
begin
723
begin
724
  sumsize := 0;
724
  sumsize := 0;
725
  sumfiles := 0;
725
  sumfiles := 0;
726
  sumfiles_new := 0;
726
  sumfiles_new := 0;
727
  sumfiles_updated := 0;
727
  sumfiles_updated := 0;
728
  sumfiles_error := 0;
728
  sumfiles_error := 0;
729
  sumfiles_deleted := 0;
729
  sumfiles_deleted := 0;
730
  sumfiles_integrityfail := 0;
730
  sumfiles_integrityfail := 0;
731
 
731
 
732
  Label1.Caption := 'Please wait...';
732
  Label1.Caption := 'Please wait...';
733
  Label5.Caption := '0';
733
  Label5.Caption := '0';
734
  Label6.Caption := '0';
734
  Label6.Caption := '0';
735
  Label7.Caption := '0';
735
  Label7.Caption := '0';
736
  Label9.Caption := '0';
736
  Label9.Caption := '0';
737
  Label11.Caption := '0';
737
  Label11.Caption := '0';
738
  Label12.Caption := '0';
738
  Label12.Caption := '0';
739
  Application.ProcessMessages;
739
  Application.ProcessMessages;
740
 
740
 
741
  EnableDisableControls(false);
741
  EnableDisableControls(false);
742
  try
742
  try
743
    if not SysUtils.DirectoryExists(LabeledEdit2.Text) then
743
    if not SysUtils.DirectoryExists(LabeledEdit2.Text) then
744
    begin
744
    begin
745
      raise Exception.CreateFmt('Directory %s not found.', [LabeledEdit2.Text]);
745
      raise Exception.CreateFmt('Directory %s not found.', [LabeledEdit2.Text]);
746
    end;
746
    end;
747
 
747
 
748
    IndexDrive(LabeledEdit2.Text);
748
    IndexDrive(LabeledEdit2.Text);
749
 
749
 
750
    (*
750
    (*
751
      if not Application.Terminated or StopRequest then
751
      if not Application.Terminated or StopRequest then
752
      begin
752
      begin
753
      ShowMessage('Finished');
753
      ShowMessage('Finished');
754
      end;
754
      end;
755
    *)
755
    *)
756
  finally
756
  finally
757
    EnableDisableControls(true);
757
    if not StopRequest then EnableDisableControls(true);
758
  end;
758
  end;
759
 
759
 
-
 
760
  if not StopRequest then
-
 
761
  begin
760
  Beep;
762
    Beep;
761
  Label1.Caption := 'Done.';
763
    Label1.Caption := 'Done.';
762
  Application.ProcessMessages;
764
    Application.ProcessMessages;
763
end;
765
  end;
-
 
766
end;
764
 
767
 
765
procedure TfrmIndexCreator.FormClose(Sender: TObject; var Action: TCloseAction);
768
procedure TfrmIndexCreator.FormClose(Sender: TObject; var Action: TCloseAction);
766
begin
769
begin
767
  StopRequest := true;
770
  StopRequest := true;
768
  Action := caFree; // TODO: müssen wir warten bis der prozess angehalten ist?
771
  Action := caFree; // TODO: müssen wir warten bis der prozess angehalten ist?
769
end;
772
end;
770
 
773
 
771
procedure TfrmIndexCreator.FormShow(Sender: TObject);
774
procedure TfrmIndexCreator.FormShow(Sender: TObject);
772
var
775
var
773
  ini: TMemIniFile;
776
  ini: TMemIniFile;
774
begin
777
begin
775
  ini := frmMain.ini;
778
  ini := frmMain.ini;
776
  rgModus.ItemIndex := ini.ReadInteger('IndexCreator', 'DefaultMode', modusUpdate);
779
  rgModus.ItemIndex := ini.ReadInteger('IndexCreator', 'DefaultMode', modusUpdate);
777
  cbNoDelete.Checked := ini.ReadBool('IndexCreator', 'DefaultCheckVanished', false);
780
  cbNoDelete.Checked := ini.ReadBool('IndexCreator', 'DefaultCheckVanished', false);
778
  cbVerboseLogs.Checked := ini.ReadBool('IndexCreator', 'DefaultVerboseLogs', false);
781
  cbVerboseLogs.Checked := ini.ReadBool('IndexCreator', 'DefaultVerboseLogs', false);
779
  cbSimulate.Checked := ini.ReadBool('IndexCreator', 'DefaultSimulate', false);
782
  cbSimulate.Checked := ini.ReadBool('IndexCreator', 'DefaultSimulate', false);
780
  LabeledEdit2.Text := ini.ReadString('IndexCreator', 'DefaultDir', 'C:\');
783
  LabeledEdit2.Text := ini.ReadString('IndexCreator', 'DefaultDir', 'C:\');
781
end;
784
end;
782
 
785
 
783
procedure TfrmIndexCreator.Button2Click(Sender: TObject);
786
procedure TfrmIndexCreator.Button2Click(Sender: TObject);
784
begin
787
begin
785
  StopRequest := true;
788
  StopRequest := true;
786
  Close;
789
  Close;
787
end;
790
end;
788
 
791
 
789
procedure TfrmIndexCreator.Button4Click(Sender: TObject);
792
procedure TfrmIndexCreator.Button4Click(Sender: TObject);
790
var
793
var
791
  i: Integer;
794
  i: Integer;
792
  s: string;
795
  s: string;
793
begin
796
begin
794
  sumsize := 0;
797
  sumsize := 0;
795
  sumfiles := 0;
798
  sumfiles := 0;
796
  sumfiles_new := 0;
799
  sumfiles_new := 0;
797
  sumfiles_updated := 0;
800
  sumfiles_updated := 0;
798
  sumfiles_error := 0;
801
  sumfiles_error := 0;
799
  sumfiles_deleted := 0;
802
  sumfiles_deleted := 0;
800
 
803
 
801
  Label1.Caption := 'Please wait...';
804
  Label1.Caption := 'Please wait...';
802
  Label5.Caption := '0';
805
  Label5.Caption := '0';
803
  Label6.Caption := '0';
806
  Label6.Caption := '0';
804
  Label7.Caption := '0';
807
  Label7.Caption := '0';
805
  Label9.Caption := '0';
808
  Label9.Caption := '0';
806
  Label11.Caption := '0';
809
  Label11.Caption := '0';
807
  Label12.Caption := '0';
810
  Label12.Caption := '0';
808
  Application.ProcessMessages;
811
  Application.ProcessMessages;
809
 
812
 
810
  EnableDisableControls(false);
813
  EnableDisableControls(false);
811
  try
814
  try
812
    // if fileexists('tmp') then memo1.lines.LoadFromFile('tmp');
815
    // if fileexists('tmp') then memo1.lines.LoadFromFile('tmp');
813
    for i := Memo1.Lines.Count - 1 downto 0 do
816
    for i := Memo1.Lines.Count - 1 downto 0 do
814
    begin
817
    begin
815
      s := Memo1.Lines.strings[i];
818
      s := Memo1.Lines.strings[i];
816
      if Trim(s) <> '' then
819
      if Trim(s) <> '' then
817
      begin
820
      begin
818
        LabeledEdit2.Text := s;
821
        LabeledEdit2.Text := s;
819
 
822
 
820
        if not SysUtils.DirectoryExists(LabeledEdit2.Text) then
823
        if not SysUtils.DirectoryExists(LabeledEdit2.Text) then
821
        begin
824
        begin
822
          raise Exception.CreateFmt('Directory %s not found.',
825
          raise Exception.CreateFmt('Directory %s not found.',
823
            [LabeledEdit2.Text]);
826
            [LabeledEdit2.Text]);
824
        end;
827
        end;
825
 
828
 
826
        IndexDrive(LabeledEdit2.Text);
829
        IndexDrive(LabeledEdit2.Text);
827
      end;
830
      end;
828
      Memo1.Lines.Delete(i);
831
      Memo1.Lines.Delete(i);
829
      // memo1.lines.SaveToFile('tmp');
832
      // memo1.lines.SaveToFile('tmp');
830
    end;
833
    end;
831
 
834
 
832
    (*
835
    (*
833
      if not Application.Terminated or StopRequest then
836
      if not Application.Terminated or StopRequest then
834
      begin
837
      begin
835
      ShowMessage('Finished');
838
      ShowMessage('Finished');
836
      end;
839
      end;
837
    *)
840
    *)
838
  finally
841
  finally
839
    EnableDisableControls(true);
842
    EnableDisableControls(true);
840
  end;
843
  end;
841
 
844
 
842
  Beep;
845
  Beep;
843
  Label1.Caption := 'Done.';
846
  Label1.Caption := 'Done.';
844
  Application.ProcessMessages;
847
  Application.ProcessMessages;
845
end;
848
end;
846
 
849
 
847
procedure TfrmIndexCreator.EnableDisableControls(enabled: boolean);
850
procedure TfrmIndexCreator.EnableDisableControls(enabled: boolean);
848
begin
851
begin
849
  rgModus.enabled := enabled;
852
  rgModus.enabled := enabled;
850
  cbNoDelete.enabled := enabled and (rgModus.ItemIndex <> modusValidation);
853
  cbNoDelete.enabled := enabled and (rgModus.ItemIndex <> modusValidation);
851
  cbVerboseLogs.enabled := enabled;
854
  cbVerboseLogs.enabled := enabled;
852
  cbSimulate.enabled := enabled and (rgModus.ItemIndex <> modusValidation);
855
  cbSimulate.enabled := enabled and (rgModus.ItemIndex <> modusValidation);
853
  Button1.enabled := enabled;
856
  Button1.enabled := enabled;
854
  LabeledEdit2.enabled := enabled;
857
  LabeledEdit2.enabled := enabled;
855
  Memo1.enabled := enabled;
858
  Memo1.enabled := enabled;
856
  Button4.enabled := enabled;
859
  Button4.enabled := enabled;
857
end;
860
end;
858
 
861
 
859
end.
862
end.
860
 
863