Rev 8 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 8 | Rev 9 | ||
---|---|---|---|
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 | // Example of multiple drives merging to one Index |
114 | // Example of multiple drives merging to one Index |
115 | // Find out via "mountvol" command |
115 | // Find out via "mountvol" command |
116 | GUID_EHDD_A = '\\?\Volume{31e044b1-28dc-11e6-9bae-d067e54bf736}\'; |
116 | GUID_EHDD_A = '\\?\Volume{31e044b1-28dc-11e6-9bae-d067e54bf736}\'; |
117 | GUID_EHDD_B = '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\'; |
117 | GUID_EHDD_B = '\\?\Volume{560e8251-2b6a-4ab7-82fc-d03df4d93538}\'; |
118 | GUID_EHDD_R = '\\?\Volume{9d53ea3c-175c-4a8f-a7b4-7b9e6b765e58}\'; |
118 | GUID_EHDD_R = '\\?\Volume{9d53ea3c-175c-4a8f-a7b4-7b9e6b765e58}\'; |
119 | {$ENDIF} |
119 | {$ENDIF} |
120 | 120 | ||
121 | function MD5File(const filename: string): string; |
121 | function MD5File(const filename: string): string; |
122 | var |
122 | var |
123 | IdMD5: TIdHashMessageDigest5; |
123 | IdMD5: TIdHashMessageDigest5; |
124 | FS: TFileStream; |
124 | FS: TFileStream; |
125 | begin |
125 | begin |
126 | IdMD5 := TIdHashMessageDigest5.Create; |
126 | IdMD5 := TIdHashMessageDigest5.Create; |
127 | FS := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite); |
127 | FS := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite); |
128 | try |
128 | try |
129 | {$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed. |
129 | {$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed. |
130 | Result := IdMD5.HashStreamAsHex(FS); |
130 | Result := IdMD5.HashStreamAsHex(FS); |
131 | {$ELSE} |
131 | {$ELSE} |
132 | Result := IdMD5.AsHex(IdMD5.HashValue(FS)); |
132 | Result := IdMD5.AsHex(IdMD5.HashValue(FS)); |
133 | {$ENDIF} |
133 | {$ENDIF} |
134 | finally |
134 | finally |
135 | FS.Free; |
135 | FS.Free; |
136 | IdMD5.Free; |
136 | IdMD5.Free; |
137 | end; |
137 | end; |
138 | end; |
138 | end; |
139 | 139 | ||
140 | function FileMTime_UTC(const filename: string): TDateTime; |
140 | function FileMTime_UTC(const filename: string): TDateTime; |
141 | var |
141 | var |
142 | fad: TWin32FileAttributeData; |
142 | fad: TWin32FileAttributeData; |
143 | systime: SYSTEMTIME; |
143 | systime: SYSTEMTIME; |
144 | begin |
144 | begin |
145 | if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then |
145 | if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then |
146 | RaiseLastOSError; |
146 | RaiseLastOSError; |
147 | 147 | ||
148 | FileTimeToSystemTime(fad.ftLastWriteTime, systime); |
148 | FileTimeToSystemTime(fad.ftLastWriteTime, systime); |
149 | 149 | ||
150 | Result := SystemTimeToDateTime(systime); |
150 | Result := SystemTimeToDateTime(systime); |
151 | end; |
151 | end; |
152 | 152 | ||
153 | function FileCTime_UTC(const filename: string): TDateTime; |
153 | function FileCTime_UTC(const filename: string): TDateTime; |
154 | var |
154 | var |
155 | fad: TWin32FileAttributeData; |
155 | fad: TWin32FileAttributeData; |
156 | systime: SYSTEMTIME; |
156 | systime: SYSTEMTIME; |
157 | begin |
157 | begin |
158 | if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then |
158 | if not GetFileAttributesEx(PChar(filename), GetFileExInfoStandard, @fad) then |
159 | RaiseLastOSError; |
159 | RaiseLastOSError; |
160 | 160 | ||
161 | FileTimeToSystemTime(fad.ftCreationTime, systime); |
161 | FileTimeToSystemTime(fad.ftCreationTime, systime); |
162 | 162 | ||
163 | Result := SystemTimeToDateTime(systime); |
163 | Result := SystemTimeToDateTime(systime); |
164 | end; |
164 | end; |
165 | 165 | ||
166 | function GetFileSize(const AFileName: String): int64; |
166 | function GetFileSize(const AFileName: String): int64; |
167 | var |
167 | var |
168 | lFindData: TWin32FindData; |
168 | lFindData: TWin32FindData; |
169 | lHandle: Cardinal; |
169 | lHandle: Cardinal; |
170 | begin |
170 | begin |
171 | // https://www.delphipraxis.net/24331-dateigroesse-einer-beliebigen-datei-ermitteln.html |
171 | // https://www.delphipraxis.net/24331-dateigroesse-einer-beliebigen-datei-ermitteln.html |
172 | lHandle := FindFirstFile(PChar(AFileName), lFindData); |
172 | lHandle := FindFirstFile(PChar(AFileName), lFindData); |
173 | if (lHandle <> INVALID_HANDLE_VALUE) then |
173 | if (lHandle <> INVALID_HANDLE_VALUE) then |
174 | begin |
174 | begin |
175 | Result := lFindData.nFileSizeLow; |
175 | Result := lFindData.nFileSizeLow; |
176 | PCardinal(Cardinal(@Result) + SizeOf(Cardinal))^ := lFindData.nFileSizeHigh; |
176 | PCardinal(Cardinal(@Result) + SizeOf(Cardinal))^ := lFindData.nFileSizeHigh; |
177 | Windows.FindClose(lHandle); |
177 | Windows.FindClose(lHandle); |
178 | end |
178 | end |
179 | else |
179 | else |
180 | Result := 0; |
180 | Result := 0; |
181 | end; |
181 | end; |
182 | 182 | ||
183 | function IntToStr2(i: int64): string; |
183 | function IntToStr2(i: int64): string; |
184 | begin |
184 | begin |
185 | // https://www.delphipraxis.net/150464-integer-mit-tausender-trennzeichen-ausgeben.html |
185 | // https://www.delphipraxis.net/150464-integer-mit-tausender-trennzeichen-ausgeben.html |
186 | Result := Format('%.0n', [i / 1]); |
186 | Result := Format('%.0n', [i / 1]); |
187 | end; |
187 | end; |
188 | 188 | ||
189 | function ConvertBytes(Bytes: int64): string; |
189 | function ConvertBytes(Bytes: int64): string; |
190 | const |
190 | const |
191 | Description: Array [0 .. 8] of string = ('Bytes', 'KB', 'MB', 'GB', 'TB', |
191 | Description: Array [0 .. 8] of string = ('Bytes', 'KB', 'MB', 'GB', 'TB', |
192 | 'PB', 'EB', 'ZB', 'YB'); |
192 | 'PB', 'EB', 'ZB', 'YB'); |
193 | var |
193 | var |
194 | i: Integer; |
194 | i: Integer; |
195 | begin |
195 | begin |
196 | // https://stackoverflow.com/questions/30548940/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-delphi |
196 | // https://stackoverflow.com/questions/30548940/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-delphi |
197 | i := 0; |
197 | i := 0; |
198 | 198 | ||
199 | while Bytes > Power(1024, i + 1) do |
199 | while Bytes > Power(1024, i + 1) do |
200 | Inc(i); |
200 | Inc(i); |
201 | 201 | ||
202 | Result := FormatFloat('###0.##', Bytes / IntPower(1024, i)) + ' ' + |
202 | Result := FormatFloat('###0.##', Bytes / IntPower(1024, i)) + ' ' + |
203 | Description[i]; |
203 | Description[i]; |
204 | end; |
204 | end; |
205 | 205 | ||
206 | var |
206 | var |
207 | DriveGuidCache: TStringList = nil; |
207 | DriveGuidCache: TStringList = nil; |
208 | 208 | ||
209 | class function TfrmIndexCreator.DriveGuid(const Letter: char): string; |
209 | class function TfrmIndexCreator.DriveGuid(const Letter: char): string; |
210 | var |
210 | var |
211 | Buffer: array [0 .. 49] of char; |
211 | Buffer: array [0 .. 49] of char; |
212 | begin |
212 | begin |
213 | if not Assigned(DriveGuidCache) then |
213 | if not Assigned(DriveGuidCache) then |
214 | DriveGuidCache := TStringList.Create; |
214 | DriveGuidCache := TStringList.Create; |
215 | 215 | ||
216 | Result := DriveGuidCache.Values[Letter]; |
216 | Result := DriveGuidCache.Values[Letter]; |
217 | if Result = '' then |
217 | if Result = '' then |
218 | begin |
218 | begin |
219 | Win32Check(GetVolumeNameForVolumeMountPoint(PChar(Letter + ':\'), Buffer, |
219 | Win32Check(GetVolumeNameForVolumeMountPoint(PChar(Letter + ':\'), Buffer, |
220 | Length(Buffer))); |
220 | Length(Buffer))); |
221 | Result := Buffer; |
221 | Result := Buffer; |
222 | DriveGuidCache.Values[Letter] := Result; |
222 | DriveGuidCache.Values[Letter] := Result; |
223 | end; |
223 | end; |
224 | end; |
224 | end; |
225 | 225 | ||
226 | class function TfrmIndexCreator.uniqueFilename(const filename: string): string; |
226 | class function TfrmIndexCreator.uniqueFilename(const filename: string): string; |
227 | var |
227 | var |
228 | guid: string; |
228 | guid: string; |
229 | begin |
229 | begin |
230 | if Length(filename) < 2 then |
230 | if Length(filename) < 2 then |
231 | exit; |
231 | exit; |
232 | if filename[2] = ':' then |
232 | if filename[2] = ':' then |
233 | begin |
233 | begin |
234 | guid := DriveGuid(filename[1]); |
234 | guid := DriveGuid(filename[1]); |
235 | 235 | ||
236 | Result := guid + Copy(filename, 4, Length(filename) - 3); |
236 | Result := guid + Copy(filename, 4, Length(filename) - 3); |
237 | 237 | ||
238 | // result := LowerCase(result); |
238 | // result := LowerCase(result); |
239 | end |
239 | end |
240 | else |
240 | else |
241 | Result := filename; // z.B. UNC-Pfad |
241 | Result := filename; // z.B. UNC-Pfad |
242 | end; |
242 | end; |
243 | 243 | ||
244 | class function TfrmIndexCreator.VtsSpecial(const filename: string): string; |
244 | class function TfrmIndexCreator.VtsSpecial(const filename: string): string; |
245 | begin |
245 | begin |
246 | Result := filename; |
246 | Result := filename; |
247 | {$IFDEF VIATHINKSOFT} |
247 | {$IFDEF VIATHINKSOFT} |
248 | Result := StringReplace(Result, GUID_EHDD_A, 'EHDD:\', []); |
248 | Result := StringReplace(Result, GUID_EHDD_A, 'EHDD:\', []); |
249 | Result := StringReplace(Result, GUID_EHDD_B, 'EHDD:\', []); |
249 | Result := StringReplace(Result, GUID_EHDD_B, 'EHDD:\', []); |
250 | Result := StringReplace(Result, GUID_EHDD_R, 'EHDD:\', []); |
250 | Result := StringReplace(Result, GUID_EHDD_R, 'EHDD:\', []); |
251 | {$ENDIF} |
251 | {$ENDIF} |
252 | end; |
252 | end; |
253 | 253 | ||
254 | function SpecialCompare(a, b: TDateTime): boolean; // true = same timestamp |
254 | function SpecialCompare(a, b: TDateTime): boolean; // true = same timestamp |
255 | begin |
255 | begin |
256 | if SecondsBetween(a,b) < 2 then exit(true); // equal |
256 | if SecondsBetween(a,b) < 2 then exit(true); // equal |
257 | 257 | ||
258 | if SecondsBetween(a,b) > 7200 then exit(false); |
258 | if SecondsBetween(a,b) > 7200 then exit(false); |
259 | 259 | ||
260 | // Minute and Second equal, and difference is < 2h: fair enough, seems to be a DST issue |
260 | // Minute and Second equal, and difference is < 2h: fair enough, seems to be a DST issue |
261 | if copy(TimeToStr(a),4,5) = copy(TimeToStr(b),4,5) then exit(true); |
261 | if copy(TimeToStr(a),4,5) = copy(TimeToStr(b),4,5) then exit(true); |
262 | 262 | ||
263 | result := false; |
263 | result := false; |
264 | end; |
264 | end; |
265 | 265 | ||
266 | procedure TfrmIndexCreator.CheckFile(const originalFileName, |
266 | procedure TfrmIndexCreator.CheckFile(const originalFileName, |
267 | uniqueFilename: string); |
267 | uniqueFilename: string); |
268 | 268 | ||
269 | function DateTimeToSQL(dt: TDateTime): string; |
269 | function DateTimeToSQL(dt: TDateTime): string; |
270 | begin |
270 | begin |
271 | if dt = -1 then |
271 | if dt = -1 then |
272 | Result := 'NULL' |
272 | Result := 'NULL' |
273 | else |
273 | else |
274 | Result := conn.SQLStringEscape(DateTimetoStr(dt)); |
274 | Result := conn.SQLStringEscape(DateTimetoStr(dt)); |
275 | end; |
275 | end; |
276 | 276 | ||
277 | type |
277 | type |
278 | TExistResult = (erDoesNotExist, erHadError, erChanged, erUnchanged); |
278 | TExistResult = (erDoesNotExist, erHadError, erChanged, erUnchanged); |
279 | 279 | ||
280 | var |
280 | var |
281 | lastCheckedMd5: string; |
281 | lastCheckedMd5: string; |
282 | 282 | ||
283 | function Exists(const filename: string; size: int64; |
283 | function Exists(const filename: string; size: int64; |
284 | const modified: TDateTime): TExistResult; |
284 | const modified: TDateTime): TExistResult; |
285 | var |
285 | var |
286 | q: TADODataSet; |
286 | q: TADODataSet; |
287 | begin |
287 | begin |
288 | q := conn.GetTable('select error, size, modified, md5hash from ' + TableName |
288 | q := conn.GetTable('select error, size, modified, md5hash from ' + TableName |
289 | + ' where filename = ' + conn.SQLStringEscape |
289 | + ' where filename = ' + conn.SQLStringEscape |
290 | (VtsSpecial(uniqueFilename))); |
290 | (VtsSpecial(uniqueFilename))); |
291 | try |
291 | try |
292 | if q.RecordCount = 0 then |
292 | if q.RecordCount = 0 then |
293 | Result := erDoesNotExist |
293 | Result := erDoesNotExist |
294 | else if not q.Fields[0].IsNull then |
294 | else if not q.Fields[0].IsNull then |
295 | Result := erHadError |
295 | Result := erHadError |
296 | else if (q.Fields[1].AsString <> IntToStr(size)) or // we are combining strings because of int64 |
296 | else if (q.Fields[1].AsString <> IntToStr(size)) or // we are combining strings because of int64 |
297 | not SpecialCompare(q.Fields[2].AsDateTime, modified) then |
297 | not SpecialCompare(q.Fields[2].AsDateTime, modified) then |
298 | begin |
298 | begin |
299 | Result := erChanged |
299 | Result := erChanged |
300 | end |
300 | end |
301 | else |
301 | else |
302 | Result := erUnchanged; |
302 | Result := erUnchanged; |
303 | lastCheckedMd5 := q.Fields[3].AsString; |
303 | lastCheckedMd5 := q.Fields[3].AsString; |
304 | finally |
304 | finally |
305 | FreeAndNil(q); |
305 | FreeAndNil(q); |
306 | end; |
306 | end; |
307 | end; |
307 | end; |
308 | 308 | ||
309 | var |
309 | var |
310 | created, modified: TDateTime; |
310 | created, modified: TDateTime; |
311 | size: int64; |
311 | size: int64; |
312 | md5: string; |
312 | md5: string; |
313 | begin |
313 | begin |
314 | Label1.Caption := MinimizeName(originalFileName, Label1.Canvas, Label1.Width); |
314 | Label1.Caption := MinimizeName(originalFileName, Label1.Canvas, Label1.Width); |
315 | Application.ProcessMessages; |
315 | Application.ProcessMessages; |
316 | 316 | ||
317 | try |
317 | try |
318 | if FileExists(uniqueFilename) then |
318 | if FileExists(uniqueFilename) then |
319 | created := FileCTime_UTC(uniqueFilename) |
319 | created := FileCTime_UTC(uniqueFilename) |
320 | else |
320 | else |
321 | created := -1; |
321 | created := -1; |
322 | 322 | ||
323 | if FileExists(uniqueFilename) then |
323 | if FileExists(uniqueFilename) then |
324 | modified := FileMTime_UTC(uniqueFilename) |
324 | modified := FileMTime_UTC(uniqueFilename) |
325 | else |
325 | else |
326 | modified := -1; |
326 | modified := -1; |
327 | 327 | ||
328 | size := GetFileSize(uniqueFilename); |
328 | size := GetFileSize(uniqueFilename); |
329 | Inc(sumsize, size); |
329 | Inc(sumsize, size); |
330 | Inc(sumfiles); |
330 | Inc(sumfiles); |
331 | 331 | ||
332 | if rgModus.ItemIndex = modusRecreate then |
332 | if rgModus.ItemIndex = modusRecreate then |
333 | begin |
333 | begin |
334 | md5 := MD5File(uniqueFilename); |
334 | md5 := MD5File(uniqueFilename); |
335 | if not cbSimulate.Checked then |
335 | if not cbSimulate.Checked then |
336 | begin |
336 | begin |
337 | conn.ExecSQL('INSERT INTO ' + TableName + |
337 | conn.ExecSQL('INSERT INTO ' + TableName + |
338 | ' (filename, size, created, modified, md5hash, error) values (' + |
338 | ' (filename, size, created, modified, md5hash, error) values (' + |
339 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' + |
339 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' + |
340 | IntToStr(size) + ', ' + DateTimeToSQL(created) + |
340 | IntToStr(size) + ', ' + DateTimeToSQL(created) + |
341 | ', ' + DateTimeToSQL(modified) + ', ' + |
341 | ', ' + DateTimeToSQL(modified) + ', ' + |
342 | conn.SQLStringEscape(LowerCase(md5)) + ', NULL);'); |
342 | conn.SQLStringEscape(LowerCase(md5)) + ', NULL);'); |
343 | end; |
343 | end; |
344 | if cbVerboseLogs.Checked then |
344 | if cbVerboseLogs.Checked then |
345 | Memo2.Lines.Add('New: ' + uniqueFilename); |
345 | Memo2.Lines.Add('New: ' + uniqueFilename); |
346 | Inc(sumfiles_new); |
346 | Inc(sumfiles_new); |
347 | end |
347 | end |
348 | else |
348 | else |
349 | begin |
349 | begin |
350 | case Exists(uniqueFilename, size, modified) of |
350 | case Exists(uniqueFilename, size, modified) of |
351 | erDoesNotExist: // File does not exist or has a different hash |
351 | erDoesNotExist: // File does not exist or has a different hash |
352 | begin |
352 | begin |
353 | if rgModus.ItemIndex <> modusValidation then |
353 | if rgModus.ItemIndex <> modusValidation then |
354 | md5 := MD5File(uniqueFilename); |
354 | md5 := MD5File(uniqueFilename); |
355 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
355 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
356 | then |
356 | then |
357 | begin |
357 | begin |
358 | conn.ExecSQL('INSERT INTO ' + TableName + |
358 | conn.ExecSQL('INSERT INTO ' + TableName + |
359 | ' (filename, size, created, modified, md5hash, error) values (' |
359 | ' (filename, size, created, modified, md5hash, error) values (' |
360 | + conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' + |
360 | + conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ', ' + |
361 | IntToStr(size) + ', ' + |
361 | IntToStr(size) + ', ' + |
362 | DateTimeToSQL(created) + ', ' + |
362 | DateTimeToSQL(created) + ', ' + |
363 | DateTimeToSQL(modified) + ', ' + |
363 | DateTimeToSQL(modified) + ', ' + |
364 | conn.SQLStringEscape(LowerCase(md5)) + ', NULL);'); |
364 | conn.SQLStringEscape(LowerCase(md5)) + ', NULL);'); |
365 | end; |
365 | end; |
366 | if cbVerboseLogs.Checked then |
366 | if cbVerboseLogs.Checked then |
367 | Memo2.Lines.Add('New: ' + uniqueFilename); |
367 | Memo2.Lines.Add('New: ' + uniqueFilename); |
368 | Inc(sumfiles_new); |
368 | Inc(sumfiles_new); |
369 | end; |
369 | end; |
370 | erHadError, erChanged: |
370 | erHadError, erChanged: |
371 | begin |
371 | begin |
372 | if rgModus.ItemIndex <> modusValidation then |
372 | if rgModus.ItemIndex <> modusValidation then |
373 | md5 := MD5File(uniqueFilename); |
373 | md5 := MD5File(uniqueFilename); |
374 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
374 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
375 | then |
375 | then |
376 | begin |
376 | begin |
377 | conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' + |
377 | conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' + |
378 | IntToStr(size) + ', created = ' + |
378 | IntToStr(size) + ', created = ' + |
379 | DateTimeToSQL(created) + ', modified = ' + |
379 | DateTimeToSQL(created) + ', modified = ' + |
380 | DateTimeToSQL(modified) + ', md5hash = ' + |
380 | DateTimeToSQL(modified) + ', md5hash = ' + |
381 | conn.SQLStringEscape(LowerCase(md5)) + |
381 | conn.SQLStringEscape(LowerCase(md5)) + |
382 | ', error = NULL WHERE filename = ' + conn.SQLStringEscape |
382 | ', error = NULL WHERE filename = ' + conn.SQLStringEscape |
383 | (VtsSpecial(uniqueFilename)) + ';'); |
383 | (VtsSpecial(uniqueFilename)) + ';'); |
384 | end; |
384 | end; |
385 | if cbVerboseLogs.Checked then |
385 | if cbVerboseLogs.Checked then |
386 | Memo2.Lines.Add('Updated: ' + uniqueFilename); |
386 | Memo2.Lines.Add('Updated: ' + uniqueFilename); |
387 | Inc(sumfiles_updated); |
387 | Inc(sumfiles_updated); |
388 | end; |
388 | end; |
389 | erUnchanged: // Date/Time+Size has not changed |
389 | erUnchanged: // Date/Time+Size has not changed |
390 | begin |
390 | begin |
391 | {$REGION 'Update it to correct wrong UTC/DST datasets...'} |
391 | {$REGION 'Update it to correct wrong UTC/DST datasets...'} |
392 | conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' + |
392 | conn.ExecSQL('UPDATE ' + TableName + ' SET size = ' + |
393 | IntToStr(size) + ', created = ' + |
393 | IntToStr(size) + ', created = ' + |
394 | DateTimeToSQL(created) + ', modified = ' + |
394 | DateTimeToSQL(created) + ', modified = ' + |
395 | DateTimeToSQL(modified) + |
395 | DateTimeToSQL(modified) + |
396 | ', error = NULL WHERE filename = ' + conn.SQLStringEscape |
396 | ', error = NULL WHERE filename = ' + conn.SQLStringEscape |
397 | (VtsSpecial(uniqueFilename)) + ';'); |
397 | (VtsSpecial(uniqueFilename)) + ';'); |
398 | {$ENDREGION} |
398 | {$ENDREGION} |
399 | 399 | ||
400 | if rgModus.ItemIndex = modusValidation then |
400 | if rgModus.ItemIndex = modusValidation then |
401 | begin |
401 | begin |
402 | md5 := MD5File(uniqueFilename); |
402 | md5 := MD5File(uniqueFilename); |
403 | if not SameText(md5, lastCheckedMd5) then |
403 | if not SameText(md5, lastCheckedMd5) then |
404 | begin |
404 | begin |
405 | Memo2.Lines.Add |
405 | Memo2.Lines.Add |
406 | ('!!! HASH HAS CHANGED WHILE DATETIME+SIZE IS THE SAME: ' + |
406 | ('!!! HASH HAS CHANGED WHILE DATETIME+SIZE IS THE SAME: ' + |
407 | uniqueFilename + ' (' + lastCheckedMd5 + ' became ' + |
407 | uniqueFilename + ' (' + lastCheckedMd5 + ' became ' + |
408 | md5 + ')'); |
408 | md5 + ')'); |
409 | Memo2.Color := clRed; |
409 | Memo2.Color := clRed; |
410 | Inc(sumfiles_integrityfail); |
410 | Inc(sumfiles_integrityfail); |
411 | end; |
411 | end; |
412 | end; |
412 | end; |
413 | end; |
413 | end; |
414 | end; |
414 | end; |
415 | end; |
415 | end; |
416 | except |
416 | except |
417 | on E: Exception do |
417 | on E: Exception do |
418 | begin |
418 | begin |
419 | if E is EAbort then |
419 | if E is EAbort then |
420 | Abort; |
420 | Abort; |
421 | // if AdoConnection1.InTransaction then AdoConnection1.RollbackTrans; |
421 | // if AdoConnection1.InTransaction then AdoConnection1.RollbackTrans; |
422 | // AdoConnection1.BeginTrans; |
422 | // AdoConnection1.BeginTrans; |
423 | try |
423 | try |
424 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
424 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
425 | then |
425 | then |
426 | begin |
426 | begin |
427 | conn.ExecSQL('DELETE FROM ' + TableName + ' WHERE filename = ' + |
427 | conn.ExecSQL('DELETE FROM ' + TableName + ' WHERE filename = ' + |
428 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ';'); |
428 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + ';'); |
429 | conn.ExecSQL('INSERT INTO ' + TableName + |
429 | conn.ExecSQL('INSERT INTO ' + TableName + |
430 | ' (filename, size, created, modified, md5hash, error) values (' + |
430 | ' (filename, size, created, modified, md5hash, error) values (' + |
431 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + |
431 | conn.SQLStringEscape(VtsSpecial(uniqueFilename)) + |
432 | ', NULL, NULL, NULL, NULL, ' + conn.SQLStringEscape(Copy(E.Message, |
432 | ', NULL, NULL, NULL, NULL, ' + conn.SQLStringEscape(Copy(E.Message, |
433 | 1, ERROR_FIELD_SIZE)) + ');'); |
433 | 1, ERROR_FIELD_SIZE)) + ');'); |
434 | Memo2.Lines.Add('Error (logged): ' + E.Message + ' at file ' + |
434 | Memo2.Lines.Add('Error (logged): ' + E.Message + ' at file ' + |
435 | VtsSpecial(uniqueFilename)); |
435 | VtsSpecial(uniqueFilename)); |
436 | end |
436 | end |
437 | else |
437 | else |
438 | begin |
438 | begin |
439 | Memo2.Lines.Add('Error: ' + E.Message + ' at file ' + |
439 | Memo2.Lines.Add('Error: ' + E.Message + ' at file ' + |
440 | VtsSpecial(uniqueFilename)); |
440 | VtsSpecial(uniqueFilename)); |
441 | end; |
441 | end; |
442 | // AdoConnection1.CommitTrans; |
442 | // AdoConnection1.CommitTrans; |
443 | except |
443 | except |
444 | // AdoConnection1.RollbackTrans; |
444 | // AdoConnection1.RollbackTrans; |
445 | Memo2.Lines.Add('Cannot write error into file database! ' + E.Message + |
445 | Memo2.Lines.Add('Cannot write error into file database! ' + E.Message + |
446 | ' at file ' + VtsSpecial(uniqueFilename)); |
446 | ' at file ' + VtsSpecial(uniqueFilename)); |
447 | end; |
447 | end; |
448 | Inc(sumfiles_error); |
448 | Inc(sumfiles_error); |
449 | end; |
449 | end; |
450 | end; |
450 | end; |
451 | 451 | ||
452 | RedrawStats; |
452 | RedrawStats; |
453 | Application.ProcessMessages; |
453 | Application.ProcessMessages; |
454 | end; |
454 | end; |
455 | 455 | ||
456 | function TfrmIndexCreator.conn: TAdoConnection; |
456 | function TfrmIndexCreator.conn: TAdoConnection; |
457 | begin |
457 | begin |
458 | Result := frmMain.AdoConnection1; |
458 | Result := frmMain.AdoConnection1; |
459 | end; |
459 | end; |
460 | 460 | ||
461 | procedure TfrmIndexCreator.RedrawStats; |
461 | procedure TfrmIndexCreator.RedrawStats; |
462 | begin |
462 | begin |
463 | Label5.Caption := ConvertBytes(sumsize); |
463 | Label5.Caption := ConvertBytes(sumsize); |
464 | Label6.Caption := IntToStr2(sumfiles); |
464 | Label6.Caption := IntToStr2(sumfiles); |
465 | Label7.Caption := IntToStr2(sumfiles_new); |
465 | Label7.Caption := IntToStr2(sumfiles_new); |
466 | Label9.Caption := IntToStr2(sumfiles_updated); |
466 | Label9.Caption := IntToStr2(sumfiles_updated); |
467 | Label11.Caption := IntToStr2(sumfiles_error); |
467 | Label11.Caption := IntToStr2(sumfiles_error); |
468 | Label12.Caption := IntToStr2(sumfiles_deleted); |
468 | Label12.Caption := IntToStr2(sumfiles_deleted); |
469 | // LabelXX.Caption := IntToStr2(sumfiles_integrityfail); |
469 | // LabelXX.Caption := IntToStr2(sumfiles_integrityfail); |
470 | end; |
470 | end; |
471 | 471 | ||
472 | procedure TfrmIndexCreator.Copyuniquepathtoclipboard1Click(Sender: TObject); |
472 | procedure TfrmIndexCreator.Copyuniquepathtoclipboard1Click(Sender: TObject); |
473 | var |
473 | var |
474 | s: string; |
474 | s: string; |
475 | begin |
475 | begin |
476 | s := uniqueFilename(LabeledEdit2.Text); |
476 | s := uniqueFilename(LabeledEdit2.Text); |
477 | Clipboard.AsText := s; |
477 | Clipboard.AsText := s; |
478 | {$IFDEF VIATHINKSOFT} |
478 | {$IFDEF VIATHINKSOFT} |
479 | if VtsSpecial(s) <> s then |
479 | if VtsSpecial(s) <> s then |
480 | begin |
480 | begin |
481 | s := s + #13#10 + VtsSpecial(s); |
481 | s := s + #13#10 + VtsSpecial(s); |
482 | end; |
482 | end; |
483 | {$ENDIF} |
483 | {$ENDIF} |
484 | ShowMessageFmt('Copied to clipboard:' + #13#10#13#10 + '%s', [s]); |
484 | ShowMessageFmt('Copied to clipboard:' + #13#10#13#10 + '%s', [s]); |
485 | end; |
485 | end; |
486 | 486 | ||
487 | procedure TfrmIndexCreator.rgModusClick(Sender: TObject); |
487 | procedure TfrmIndexCreator.rgModusClick(Sender: TObject); |
488 | begin |
488 | begin |
489 | cbSimulate.enabled := rgModus.ItemIndex <> modusValidation; |
489 | cbSimulate.enabled := rgModus.ItemIndex <> modusValidation; |
490 | cbNoDelete.enabled := rgModus.ItemIndex <> modusValidation; |
490 | cbNoDelete.enabled := rgModus.ItemIndex <> modusValidation; |
491 | end; |
491 | end; |
492 | 492 | ||
493 | function TfrmIndexCreator.TableName: string; |
493 | function TfrmIndexCreator.TableName: string; |
494 | begin |
494 | begin |
495 | Result := frmMain.TableName; |
495 | Result := frmMain.TableName; |
496 | end; |
496 | end; |
497 | 497 | ||
498 | procedure TfrmIndexCreator.Rec(StartDir: string; const FileMask: string); |
498 | procedure TfrmIndexCreator.Rec(StartDir: string; const FileMask: string); |
499 | var |
499 | var |
500 | SR: TSearchRec; |
500 | SR: TSearchRec; |
501 | DirList: TStrings; |
501 | DirList: TStrings; |
502 | IsFound: boolean; |
502 | IsFound: boolean; |
503 | i: Integer; |
503 | i: Integer; |
504 | UniqueStartDir: string; |
504 | UniqueStartDir: string; |
505 | begin |
505 | begin |
506 | StartDir := IncludeTrailingPathDelimiter(StartDir); |
506 | StartDir := IncludeTrailingPathDelimiter(StartDir); |
507 | 507 | ||
508 | i := 0; |
508 | i := 0; |
509 | conn.BeginTrans; |
509 | conn.BeginTrans; |
510 | IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0; |
510 | IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0; |
511 | try |
511 | try |
512 | while IsFound do |
512 | while IsFound do |
513 | begin |
513 | begin |
514 | Inc(i); |
514 | Inc(i); |
515 | if i mod 1000 = 0 then // Only for performance |
515 | if i mod 1000 = 0 then // Only for performance |
516 | begin |
516 | begin |
517 | conn.CommitTrans; |
517 | conn.CommitTrans; |
518 | conn.BeginTrans; |
518 | conn.BeginTrans; |
519 | end; |
519 | end; |
520 | Application.ProcessMessages; |
520 | Application.ProcessMessages; |
521 | if Application.Terminated or StopRequest then |
521 | if Application.Terminated or StopRequest then |
522 | Abort; |
522 | Abort; |
523 | 523 | ||
524 | if UniqueStartDir = '' then |
524 | if UniqueStartDir = '' then |
525 | UniqueStartDir := uniqueFilename(StartDir); |
525 | UniqueStartDir := uniqueFilename(StartDir); |
526 | CheckFile(StartDir + SR.Name, UniqueStartDir + SR.Name); |
526 | CheckFile(StartDir + SR.Name, UniqueStartDir + SR.Name); |
527 | IsFound := FindNext(SR) = 0; |
527 | IsFound := FindNext(SR) = 0; |
528 | end; |
528 | end; |
529 | finally |
529 | finally |
530 | FindClose(SR); |
530 | FindClose(SR); |
531 | conn.CommitTrans; |
531 | conn.CommitTrans; |
532 | end; |
532 | end; |
533 | 533 | ||
534 | // Build a list of subdirectories |
534 | // Build a list of subdirectories |
535 | DirList := TStringList.Create; |
535 | DirList := TStringList.Create; |
536 | try |
536 | try |
537 | IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0; |
537 | IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0; |
538 | try |
538 | try |
539 | while IsFound do |
539 | while IsFound do |
540 | begin |
540 | begin |
541 | if (SR.Name <> '.') and (SR.Name <> '..') then |
541 | if (SR.Name <> '.') and (SR.Name <> '..') then |
542 | begin |
542 | begin |
543 | Application.ProcessMessages; |
543 | Application.ProcessMessages; |
544 | if Application.Terminated or StopRequest then |
544 | if Application.Terminated or StopRequest then |
545 | Abort; |
545 | Abort; |
546 | 546 | ||
547 | DirList.Add(StartDir + SR.Name); |
547 | DirList.Add(StartDir + SR.Name); |
548 | end; |
548 | end; |
549 | IsFound := FindNext(SR) = 0; |
549 | IsFound := FindNext(SR) = 0; |
550 | end; |
550 | end; |
551 | finally |
551 | finally |
552 | FindClose(SR); |
552 | FindClose(SR); |
553 | end; |
553 | end; |
554 | 554 | ||
555 | // Scan the list of subdirectories |
555 | // Scan the list of subdirectories |
556 | for i := 0 to DirList.Count - 1 do |
556 | for i := 0 to DirList.Count - 1 do |
557 | begin |
557 | begin |
558 | try |
558 | try |
559 | Rec(DirList[i], FileMask); |
559 | Rec(DirList[i], FileMask); |
560 | except |
560 | except |
561 | on E: Exception do |
561 | on E: Exception do |
562 | begin |
562 | begin |
563 | if E is EAbort then |
563 | if E is EAbort then |
564 | Abort; |
564 | Abort; |
565 | Memo2.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' + |
565 | Memo2.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' + |
566 | E.Message); |
566 | E.Message); |
567 | end; |
567 | end; |
568 | end; |
568 | end; |
569 | end; |
569 | end; |
570 | finally |
570 | finally |
571 | DirList.Free; |
571 | DirList.Free; |
572 | end; |
572 | end; |
573 | end; |
573 | end; |
574 | 574 | ||
575 | procedure TfrmIndexCreator.DeleteAllFiles(mask: string = ''); |
575 | procedure TfrmIndexCreator.DeleteAllFiles(mask: string = ''); |
576 | begin |
576 | begin |
577 | sumfiles_deleted := conn.GetScalar('select count(*) as cnt from ' + TableName |
577 | sumfiles_deleted := conn.GetScalar('select count(*) as cnt from ' + TableName |
578 | + ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask))); |
578 | + ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask))); |
579 | RedrawStats; |
579 | RedrawStats; |
580 | 580 | ||
581 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) then |
581 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) then |
582 | begin |
582 | begin |
583 | if (mask = '') or (mask = '%') then |
583 | if (mask = '') or (mask = '%') then |
584 | conn.ExecSQL('delete from ' + TableName) |
584 | conn.ExecSQL('delete from ' + TableName) |
585 | else |
585 | else |
586 | conn.ExecSQL('delete from ' + TableName + ' where filename like ' + |
586 | conn.ExecSQL('delete from ' + TableName + ' where filename like ' + |
587 | conn.SQLStringEscape(VtsSpecial(mask))); |
587 | conn.SQLStringEscape(VtsSpecial(mask))); |
588 | end; |
588 | end; |
589 | end; |
589 | end; |
590 | 590 | ||
591 | procedure TfrmIndexCreator.DeleteVanishedFiles(mask: string = ''); |
591 | procedure TfrmIndexCreator.DeleteVanishedFiles(mask: string = ''); |
592 | 592 | ||
593 | {$IFDEF VIATHINKSOFT} |
593 | {$IFDEF VIATHINKSOFT} |
594 | var |
594 | var |
595 | cacheAconnected: boolean; |
595 | cacheAconnected: boolean; |
596 | cacheBconnected: boolean; |
596 | cacheBconnected: boolean; |
597 | cacheRconnected: boolean; |
597 | cacheRconnected: boolean; |
598 | {$ENDIF} |
598 | {$ENDIF} |
599 | function AllowFileCheck(AFileName: string): boolean; |
599 | function AllowFileCheck(AFileName: string): boolean; |
600 | var |
600 | var |
601 | guid: string; |
601 | guid: string; |
602 | begin |
602 | begin |
603 | Result := false; |
603 | Result := false; |
604 | {$IFDEF VIATHINKSOFT} |
604 | {$IFDEF VIATHINKSOFT} |
605 | if StartsText('EHDD:\', AFileName) then |
605 | if StartsText('EHDD:\', AFileName) then |
606 | begin |
606 | begin |
607 | if not cacheAconnected and SysUtils.DirectoryExists(GUID_EHDD_A) then |
607 | if not cacheAconnected and SysUtils.DirectoryExists(GUID_EHDD_A) then |
608 | begin |
608 | begin |
609 | cacheAconnected := true; |
609 | cacheAconnected := true; |
610 | end; |
610 | end; |
611 | if not cacheBconnected and SysUtils.DirectoryExists(GUID_EHDD_B) then |
611 | if not cacheBconnected and SysUtils.DirectoryExists(GUID_EHDD_B) then |
612 | begin |
612 | begin |
613 | cacheBconnected := true; |
613 | cacheBconnected := true; |
614 | end; |
614 | end; |
615 | if not cacheRconnected and SysUtils.DirectoryExists(GUID_EHDD_R) then |
615 | if not cacheRconnected and SysUtils.DirectoryExists(GUID_EHDD_R) then |
616 | begin |
616 | begin |
617 | cacheRconnected := true; |
617 | cacheRconnected := true; |
618 | end; |
618 | end; |
619 | Result := cacheAconnected or cacheBconnected or cacheRconnected; |
619 | Result := cacheAconnected or cacheBconnected or cacheRconnected; |
620 | end |
620 | end |
621 | else |
621 | else |
622 | {$ENDIF} |
622 | {$ENDIF} |
623 | if StartsText('\\?\Volume', AFileName) then |
623 | if StartsText('\\?\Volume', AFileName) then |
624 | begin |
624 | begin |
625 | guid := Copy(AFileName, 1, 49); |
625 | guid := Copy(AFileName, 1, 49); |
626 | if EndsText('\', guid) then // should always happen |
626 | if EndsText('\', guid) then // should always happen |
627 | begin |
627 | begin |
628 | // TODO: cache this result somehow, so that DirectoryExists() does not need to be called all the time |
628 | // TODO: cache this result somehow, so that DirectoryExists() does not need to be called all the time |
629 | if SysUtils.DirectoryExists(guid) then // is drive connected/existing? |
629 | if SysUtils.DirectoryExists(guid) then // is drive connected/existing? |
630 | begin |
630 | begin |
631 | Result := true; |
631 | Result := true; |
632 | end; |
632 | end; |
633 | end; |
633 | end; |
634 | end |
634 | end |
635 | else |
635 | else |
636 | begin |
636 | begin |
637 | // TODO: Einen Code für Netzlaufwerke machen: Wir dürfen nur Dateien löschen, |
637 | // TODO: Einen Code für Netzlaufwerke machen: Wir dürfen nur Dateien löschen, |
638 | // wenn das Netzlaufwerk wirklich da ist. |
638 | // wenn das Netzlaufwerk wirklich da ist. |
639 | end; |
639 | end; |
640 | end; |
640 | end; |
641 | 641 | ||
642 | function FileDoesExist(AFileName: string): boolean; |
642 | function FileDoesExist(AFileName: string): boolean; |
643 | begin |
643 | begin |
644 | {$IFDEF VIATHINKSOFT} |
644 | {$IFDEF VIATHINKSOFT} |
645 | if StartsText('EHDD:\', AFileName) then |
645 | if StartsText('EHDD:\', AFileName) then |
646 | begin |
646 | begin |
647 | // Attention: AllowFileCheck must be called to initialize cacheAconnected and cacheBconnected |
647 | // Attention: AllowFileCheck must be called to initialize cacheAconnected and cacheBconnected and cacheRconnected |
648 | 648 | ||
649 | if cacheAconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
649 | if cacheAconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
650 | GUID_EHDD_A, [])) then |
650 | GUID_EHDD_A, [])) then |
651 | exit(true); |
651 | exit(true); |
652 | 652 | ||
653 | if cacheBconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
653 | if cacheBconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
654 | GUID_EHDD_B, [])) then |
654 | GUID_EHDD_B, [])) then |
655 | exit(true); |
655 | exit(true); |
656 | 656 | ||
657 | if cacheBconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
657 | if cacheRconnected and FileExists(StringReplace(AFileName, 'EHDD:\', |
658 | GUID_EHDD_R, [])) then |
658 | GUID_EHDD_R, [])) then |
659 | exit(true); |
659 | exit(true); |
660 | 660 | ||
661 | exit(false); |
661 | exit(false); |
662 | end; |
662 | end; |
663 | {$ENDIF} |
663 | {$ENDIF} |
664 | exit(FileExists(AFileName)); |
664 | exit(FileExists(AFileName)); |
665 | end; |
665 | end; |
666 | 666 | ||
667 | var |
667 | var |
668 | filename: string; |
668 | filename: string; |
669 | q: TADODataSet; |
669 | q: TADODataSet; |
670 | fFileName: TField; |
670 | fFileName: TField; |
671 | i: int64; |
671 | i: int64; |
672 | begin |
672 | begin |
673 | if mask <> '' then |
673 | if mask <> '' then |
674 | q := conn.GetTable('select filename from ' + TableName + |
674 | q := conn.GetTable('select filename from ' + TableName + |
675 | ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask))) |
675 | ' where filename like ' + conn.SQLStringEscape(VtsSpecial(mask))) |
676 | else |
676 | else |
677 | q := conn.GetTable('select filename from ' + TableName); |
677 | q := conn.GetTable('select filename from ' + TableName); |
678 | try |
678 | try |
679 | i := 0; |
679 | i := 0; |
680 | fFileName := q.FieldByName('filename'); |
680 | fFileName := q.FieldByName('filename'); |
681 | while not q.Eof do |
681 | while not q.Eof do |
682 | begin |
682 | begin |
683 | filename := fFileName.AsString; |
683 | filename := fFileName.AsString; |
684 | 684 | ||
685 | if AllowFileCheck(filename) and not FileDoesExist(filename) then |
685 | if AllowFileCheck(filename) and not FileDoesExist(filename) then |
686 | begin |
686 | begin |
687 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
687 | if not cbSimulate.Checked and (rgModus.ItemIndex <> modusValidation) |
688 | then |
688 | then |
689 | begin |
689 | begin |
690 | conn.ExecSQL('delete from ' + TableName + ' where filename = ' + |
690 | conn.ExecSQL('delete from ' + TableName + ' where filename = ' + |
691 | conn.SQLStringEscape(filename)); |
691 | conn.SQLStringEscape(filename)); |
692 | end; |
692 | end; |
693 | Inc(sumfiles_deleted); |
693 | Inc(sumfiles_deleted); |
694 | if cbVerboseLogs.Checked then |
694 | if cbVerboseLogs.Checked then |
695 | Memo2.Lines.Add('Deleted: ' + filename); |
695 | Memo2.Lines.Add('Deleted: ' + filename); |
696 | RedrawStats; |
696 | RedrawStats; |
697 | end; |
697 | end; |
698 | 698 | ||
699 | Inc(i); |
699 | Inc(i); |
700 | if i mod 100 = 0 then |
700 | if i mod 100 = 0 then |
701 | begin |
701 | begin |
702 | Label1.Caption := MinimizeName(filename, Label1.Canvas, Label1.Width); |
702 | Label1.Caption := MinimizeName(filename, Label1.Canvas, Label1.Width); |
703 | Application.ProcessMessages; |
703 | Application.ProcessMessages; |
704 | if Application.Terminated or StopRequest then |
704 | if Application.Terminated or StopRequest then |
705 | Abort; |
705 | Abort; |
706 | end; |
706 | end; |
707 | 707 | ||
708 | q.Next; |
708 | q.Next; |
709 | end; |
709 | end; |
710 | finally |
710 | finally |
711 | FreeAndNil(q); |
711 | FreeAndNil(q); |
712 | end; |
712 | end; |
713 | end; |
713 | end; |
714 | 714 | ||
715 | procedure TfrmIndexCreator.IndexDrive(initialdir: string); |
715 | procedure TfrmIndexCreator.IndexDrive(initialdir: string); |
716 | begin |
716 | begin |
717 | if not cbNoDelete.Checked and not cbSimulate.Checked and |
717 | if not cbNoDelete.Checked and not cbSimulate.Checked and |
718 | (rgModus.ItemIndex <> modusValidation) then |
718 | (rgModus.ItemIndex <> modusValidation) then |
719 | begin |
719 | begin |
720 | if rgModus.ItemIndex = modusRecreate then |
720 | if rgModus.ItemIndex = modusRecreate then |
721 | begin |
721 | begin |
722 | DeleteAllFiles(uniqueFilename(IncludeTrailingPathDelimiter |
722 | DeleteAllFiles(uniqueFilename(IncludeTrailingPathDelimiter |
723 | (initialdir)) + '%'); |
723 | (initialdir)) + '%'); |
724 | end |
724 | end |
725 | else |
725 | else |
726 | begin |
726 | begin |
727 | DeleteVanishedFiles |
727 | DeleteVanishedFiles |
728 | (uniqueFilename(IncludeTrailingPathDelimiter(initialdir)) + '%'); |
728 | (uniqueFilename(IncludeTrailingPathDelimiter(initialdir)) + '%'); |
729 | end; |
729 | end; |
730 | end; |
730 | end; |
731 | 731 | ||
732 | Rec(IncludeTrailingPathDelimiter(initialdir), '*'); |
732 | Rec(IncludeTrailingPathDelimiter(initialdir), '*'); |
733 | end; |
733 | end; |
734 | 734 | ||
735 | procedure TfrmIndexCreator.Button1Click(Sender: TObject); |
735 | procedure TfrmIndexCreator.Button1Click(Sender: TObject); |
736 | begin |
736 | begin |
737 | sumsize := 0; |
737 | sumsize := 0; |
738 | sumfiles := 0; |
738 | sumfiles := 0; |
739 | sumfiles_new := 0; |
739 | sumfiles_new := 0; |
740 | sumfiles_updated := 0; |
740 | sumfiles_updated := 0; |
741 | sumfiles_error := 0; |
741 | sumfiles_error := 0; |
742 | sumfiles_deleted := 0; |
742 | sumfiles_deleted := 0; |
743 | sumfiles_integrityfail := 0; |
743 | sumfiles_integrityfail := 0; |
744 | 744 | ||
745 | Label1.Caption := 'Please wait...'; |
745 | Label1.Caption := 'Please wait...'; |
746 | Label5.Caption := '0'; |
746 | Label5.Caption := '0'; |
747 | Label6.Caption := '0'; |
747 | Label6.Caption := '0'; |
748 | Label7.Caption := '0'; |
748 | Label7.Caption := '0'; |
749 | Label9.Caption := '0'; |
749 | Label9.Caption := '0'; |
750 | Label11.Caption := '0'; |
750 | Label11.Caption := '0'; |
751 | Label12.Caption := '0'; |
751 | Label12.Caption := '0'; |
752 | Application.ProcessMessages; |
752 | Application.ProcessMessages; |
753 | 753 | ||
754 | EnableDisableControls(false); |
754 | EnableDisableControls(false); |
755 | try |
755 | try |
756 | if not SysUtils.DirectoryExists(LabeledEdit2.Text) then |
756 | if not SysUtils.DirectoryExists(LabeledEdit2.Text) then |
757 | begin |
757 | begin |
758 | raise Exception.CreateFmt('Directory %s not found.', [LabeledEdit2.Text]); |
758 | raise Exception.CreateFmt('Directory %s not found.', [LabeledEdit2.Text]); |
759 | end; |
759 | end; |
760 | 760 | ||
761 | IndexDrive(LabeledEdit2.Text); |
761 | IndexDrive(LabeledEdit2.Text); |
762 | 762 | ||
763 | (* |
763 | (* |
764 | if not Application.Terminated or StopRequest then |
764 | if not Application.Terminated or StopRequest then |
765 | begin |
765 | begin |
766 | ShowMessage('Finished'); |
766 | ShowMessage('Finished'); |
767 | end; |
767 | end; |
768 | *) |
768 | *) |
769 | finally |
769 | finally |
770 | if not StopRequest then EnableDisableControls(true); |
770 | if not StopRequest then EnableDisableControls(true); |
771 | end; |
771 | end; |
772 | 772 | ||
773 | if not StopRequest then |
773 | if not StopRequest then |
774 | begin |
774 | begin |
775 | Beep; |
775 | Beep; |
776 | Label1.Caption := 'Done.'; |
776 | Label1.Caption := 'Done.'; |
777 | Application.ProcessMessages; |
777 | Application.ProcessMessages; |
778 | end; |
778 | end; |
779 | end; |
779 | end; |
780 | 780 | ||
781 | procedure TfrmIndexCreator.FormClose(Sender: TObject; var Action: TCloseAction); |
781 | procedure TfrmIndexCreator.FormClose(Sender: TObject; var Action: TCloseAction); |
782 | begin |
782 | begin |
783 | StopRequest := true; |
783 | StopRequest := true; |
784 | Action := caFree; // TODO: müssen wir warten bis der prozess angehalten ist? |
784 | Action := caFree; // TODO: müssen wir warten bis der prozess angehalten ist? |
785 | end; |
785 | end; |
786 | 786 | ||
787 | procedure TfrmIndexCreator.FormShow(Sender: TObject); |
787 | procedure TfrmIndexCreator.FormShow(Sender: TObject); |
788 | var |
788 | var |
789 | ini: TMemIniFile; |
789 | ini: TMemIniFile; |
790 | begin |
790 | begin |
791 | ini := frmMain.ini; |
791 | ini := frmMain.ini; |
792 | rgModus.ItemIndex := ini.ReadInteger('IndexCreator', 'DefaultMode', modusUpdate); |
792 | rgModus.ItemIndex := ini.ReadInteger('IndexCreator', 'DefaultMode', modusUpdate); |
793 | cbNoDelete.Checked := ini.ReadBool('IndexCreator', 'DefaultCheckVanished', false); |
793 | cbNoDelete.Checked := ini.ReadBool('IndexCreator', 'DefaultCheckVanished', false); |
794 | cbVerboseLogs.Checked := ini.ReadBool('IndexCreator', 'DefaultVerboseLogs', false); |
794 | cbVerboseLogs.Checked := ini.ReadBool('IndexCreator', 'DefaultVerboseLogs', false); |
795 | cbSimulate.Checked := ini.ReadBool('IndexCreator', 'DefaultSimulate', false); |
795 | cbSimulate.Checked := ini.ReadBool('IndexCreator', 'DefaultSimulate', false); |
796 | LabeledEdit2.Text := ini.ReadString('IndexCreator', 'DefaultDir', 'C:\'); |
796 | LabeledEdit2.Text := ini.ReadString('IndexCreator', 'DefaultDir', 'C:\'); |
797 | end; |
797 | end; |
798 | 798 | ||
799 | procedure TfrmIndexCreator.Button2Click(Sender: TObject); |
799 | procedure TfrmIndexCreator.Button2Click(Sender: TObject); |
800 | begin |
800 | begin |
801 | StopRequest := true; |
801 | StopRequest := true; |
802 | Close; |
802 | Close; |
803 | end; |
803 | end; |
804 | 804 | ||
805 | procedure TfrmIndexCreator.Button4Click(Sender: TObject); |
805 | procedure TfrmIndexCreator.Button4Click(Sender: TObject); |
806 | var |
806 | var |
807 | i: Integer; |
807 | i: Integer; |
808 | s: string; |
808 | s: string; |
809 | begin |
809 | begin |
810 | sumsize := 0; |
810 | sumsize := 0; |
811 | sumfiles := 0; |
811 | sumfiles := 0; |
812 | sumfiles_new := 0; |
812 | sumfiles_new := 0; |
813 | sumfiles_updated := 0; |
813 | sumfiles_updated := 0; |
814 | sumfiles_error := 0; |
814 | sumfiles_error := 0; |
815 | sumfiles_deleted := 0; |
815 | sumfiles_deleted := 0; |
816 | 816 | ||
817 | Label1.Caption := 'Please wait...'; |
817 | Label1.Caption := 'Please wait...'; |
818 | Label5.Caption := '0'; |
818 | Label5.Caption := '0'; |
819 | Label6.Caption := '0'; |
819 | Label6.Caption := '0'; |
820 | Label7.Caption := '0'; |
820 | Label7.Caption := '0'; |
821 | Label9.Caption := '0'; |
821 | Label9.Caption := '0'; |
822 | Label11.Caption := '0'; |
822 | Label11.Caption := '0'; |
823 | Label12.Caption := '0'; |
823 | Label12.Caption := '0'; |
824 | Application.ProcessMessages; |
824 | Application.ProcessMessages; |
825 | 825 | ||
826 | EnableDisableControls(false); |
826 | EnableDisableControls(false); |
827 | try |
827 | try |
828 | // if fileexists('tmp') then memo1.lines.LoadFromFile('tmp'); |
828 | // if fileexists('tmp') then memo1.lines.LoadFromFile('tmp'); |
829 | for i := Memo1.Lines.Count - 1 downto 0 do |
829 | for i := Memo1.Lines.Count - 1 downto 0 do |
830 | begin |
830 | begin |
831 | s := Memo1.Lines.strings[i]; |
831 | s := Memo1.Lines.strings[i]; |
832 | if Trim(s) <> '' then |
832 | if Trim(s) <> '' then |
833 | begin |
833 | begin |
834 | LabeledEdit2.Text := s; |
834 | LabeledEdit2.Text := s; |
835 | 835 | ||
836 | if not SysUtils.DirectoryExists(LabeledEdit2.Text) then |
836 | if not SysUtils.DirectoryExists(LabeledEdit2.Text) then |
837 | begin |
837 | begin |
838 | raise Exception.CreateFmt('Directory %s not found.', |
838 | raise Exception.CreateFmt('Directory %s not found.', |
839 | [LabeledEdit2.Text]); |
839 | [LabeledEdit2.Text]); |
840 | end; |
840 | end; |
841 | 841 | ||
842 | IndexDrive(LabeledEdit2.Text); |
842 | IndexDrive(LabeledEdit2.Text); |
843 | end; |
843 | end; |
844 | Memo1.Lines.Delete(i); |
844 | Memo1.Lines.Delete(i); |
845 | // memo1.lines.SaveToFile('tmp'); |
845 | // memo1.lines.SaveToFile('tmp'); |
846 | end; |
846 | end; |
847 | 847 | ||
848 | (* |
848 | (* |
849 | if not Application.Terminated or StopRequest then |
849 | if not Application.Terminated or StopRequest then |
850 | begin |
850 | begin |
851 | ShowMessage('Finished'); |
851 | ShowMessage('Finished'); |
852 | end; |
852 | end; |
853 | *) |
853 | *) |
854 | finally |
854 | finally |
855 | EnableDisableControls(true); |
855 | EnableDisableControls(true); |
856 | end; |
856 | end; |
857 | 857 | ||
858 | Beep; |
858 | Beep; |
859 | Label1.Caption := 'Done.'; |
859 | Label1.Caption := 'Done.'; |
860 | Application.ProcessMessages; |
860 | Application.ProcessMessages; |
861 | end; |
861 | end; |
862 | 862 | ||
863 | procedure TfrmIndexCreator.EnableDisableControls(enabled: boolean); |
863 | procedure TfrmIndexCreator.EnableDisableControls(enabled: boolean); |
864 | begin |
864 | begin |
865 | rgModus.enabled := enabled; |
865 | rgModus.enabled := enabled; |
866 | cbNoDelete.enabled := enabled and (rgModus.ItemIndex <> modusValidation); |
866 | cbNoDelete.enabled := enabled and (rgModus.ItemIndex <> modusValidation); |
867 | cbVerboseLogs.enabled := enabled; |
867 | cbVerboseLogs.enabled := enabled; |
868 | cbSimulate.enabled := enabled and (rgModus.ItemIndex <> modusValidation); |
868 | cbSimulate.enabled := enabled and (rgModus.ItemIndex <> modusValidation); |
869 | Button1.enabled := enabled; |
869 | Button1.enabled := enabled; |
870 | LabeledEdit2.enabled := enabled; |
870 | LabeledEdit2.enabled := enabled; |
871 | Memo1.enabled := enabled; |
871 | Memo1.enabled := enabled; |
872 | Button4.enabled := enabled; |
872 | Button4.enabled := enabled; |
873 | end; |
873 | end; |
874 | 874 | ||
875 | end. |
875 | end. |
876 | 876 |