Subversion Repositories autosfx

Rev

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

Rev Author Line No. Line
1 daniel-mar 1
unit ExtractorMain;
2
 
3
{$DEFINE USE_DZIP_UNPACK}
4
 
5
interface
6
 
7
uses
8
  Windows, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ShellAPI,
9
  ExtCtrls, ComCtrls, ZipMstr19, ZMMsgStr19, ZMMsg19, ZMDelZip19,
2 daniel-mar 10
  ZMCompat19, SFXBehavior, ShlObj;
1 daniel-mar 11
 
12
type
13
  TOverwriteDecision = (odUndefined, odOverwriteAll, odOverwriteNothing);
14
 
15
  TMainForm = class(TForm)
16
    ProgressBar: TProgressBar;
17
    WaitLabel: TLabel;
18
    CancelBtn: TButton;
19
    CurrentFileLabel: TLabel;
20
    AutoTimer: TTimer;
21
    itemBar: TProgressBar;
22
    procedure CancelBtnClick(Sender: TObject);
23
    procedure FormCreate(Sender: TObject);
24
    procedure AutoTimerTimer(Sender: TObject);
25
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
26
  private
3 daniel-mar 27
    ExtractionInProcess: boolean;
28
    uz: TZipMaster19;
1 daniel-mar 29
    RenamingOldPrefix: string;
30
    RenamingNewPrefix: string;
31
    zb: TZIPBehavior;
32
    BaseDir: string;
33
    AbortUnzip: boolean;
34
    StopAskingPassword: boolean;
35
    LastTriedPassword: string;
3 daniel-mar 36
    CachedOverwriteDecision: TOverwriteDecision;
1 daniel-mar 37
    {$IFNDEF USE_DZIP_UNPACK}
38
    procedure ExtractDllFromResource(ADirectory: string);
39
    {$ENDIF}
40
    procedure ExtractZipHere(AZipfile: string);
3 daniel-mar 41
    procedure EvExtFNChange(Sender: TObject; var FileName: TZMString; const BaseDir: TZMString; var IsChanged: Boolean);
42
    procedure EvProgress(Sender: TObject; details: TZMProgressDetails);
43
    procedure EvTick(Sender: TObject);
44
    procedure EvCheckTerminate(Sender: TObject; var abort: Boolean);
45
    procedure EvConfirmOverwrite(Sender: TObject; const ForFile: TZMString;
1 daniel-mar 46
      IsOlder: Boolean; var DoOverwrite: Boolean; DirIndex: Integer);
3 daniel-mar 47
    procedure EvPasswordEvent(Sender: TObject; IsZipAction: Boolean;
1 daniel-mar 48
      var NewPassword: String; const ForFile: TZMString; var RepeatCount: Longword;
49
      var Action: TMsgDlgBtn);
3 daniel-mar 50
    procedure EvSkipEvent(Sender: TObject; const ForFile: TZMString;
1 daniel-mar 51
      SkipType: TZMSkipTypes; var ExtError: Integer);
52
    function StripBaseDir(const s: string): string;
53
  end;
54
 
55
var
56
  MainForm: TMainForm;
57
 
58
implementation
59
 
60
uses
3 daniel-mar 61
  ExtractorPassword, ExtractorError, Functions, SFXAutoRun, ExtractorComment,
62
  BrowseFolder;
1 daniel-mar 63
 
64
const
3 daniel-mar 65
  EvPasswordTries = 15;
1 daniel-mar 66
 
67
{$R *.dfm}
68
 
69
{$R zmstr1900102\DLL\ResDLL-Maker\ZMRes19_dll.res}
70
 
71
function IsRootFile(s: string): boolean;
72
var
73
  i: integer;
74
begin
75
  if s = '' then
76
  begin
77
    result := false;
78
    Exit;
79
  end;
80
  if LooksLikeDir(s) then
81
  begin
82
    s := Copy(s, 1, Length(s)-1);
83
  end;
84
  for i := 1 to Length(s) do
85
  begin
86
    if s[i] = PathDelim then
87
    begin
88
      result := false;
89
      Exit;
90
    end;
91
  end;
92
  result := true;
93
end;
94
 
95
function FEListAll(theRec: TZMDirEntry; var Data): Integer;
96
var
97
  l: TStringList absolute Data;
98
  s: string;
99
begin
100
  Result := 0;
101
 
102
  s := theRec.FileName;
103
  l.Add(s);
104
 
105
  s := ExtractFilePath(s);
106
  if l.IndexOf(s) = -1 then l.Add(s);
107
end;
108
 
109
procedure ListRootFiles(uz: TZipMaster19; List: TStringList);
110
var
111
  s: string;
112
  l: TStringList;
113
  i: Integer;
114
resourcestring
115
  Lng_ForeachFailed = 'Dateiiteration fehlgeschlagen!';
116
begin
117
  List.Clear;
118
 
119
  l := TStringList.Create;
120
  try
121
    // Fill list (inclusive Directories)
122
 
123
    uz.FSpecArgs.Add('*');
124
    if uz.ForEach(FEListAll, l) <> 0 then
125
    begin
126
      MessageDlg(Lng_ForeachFailed, mtError, [mbOk], 0);
127
    end;
128
 
129
    // Now look for root files
130
 
131
    for i := 0 to l.Count - 1 do
132
    begin
133
      s := l.Strings[i];
134
 
135
      if IsRootFile(s) then
136
      begin
137
        List.Add(s);
138
      end;
139
    end;
140
  finally
141
    l.Free;
142
  end;
143
end;
144
 
145
procedure TMainForm.ExtractZipHere(AZipfile: string);
146
var
147
  l: TStringList;
148
  s: string;
149
  ec: Integer;
150
  ar: TExecuteSFXAutoRunResult;
2 daniel-mar 151
  GeneralBaseDir: string;
3 daniel-mar 152
const
153
  C_Explorer_Open_Param = '"%s"';
154
  C_Explorer_Select_Param = '/n,/select,"%s"';
155
  EXPLORER_EXE = 'explorer';
1 daniel-mar 156
resourcestring
157
  Lng_Aborted = 'Der laufende Prozess wurde abgebrochen. Das extrahierten Dateien sind somit unvollständig.';
158
  Lng_Zip_Error = 'ZIP-Master Fehler "%s" (%d)';
159
  Lng_AutoRunFailed = 'SFX-AutoRun fehlgeschlagen. Die entpackten Inhalte werden nun angezeigt.';
160
  Lng_Unknown_Error = 'Unbekannter Fehler: Dateien sind nicht aufzufinden!';
2 daniel-mar 161
  Lng_SelectDir = 'Bitte wählen Sie ein Verzeichnis zum Extrahieren aus. Es wird maximal 1 Datei bzw. Ordner erstellt!';
1 daniel-mar 162
begin
163
  AZipfile := ExpandUNCFileName(AZipfile);
164
  RenamingOldPrefix := '';
165
  RenamingNewPrefix := '';
166
 
3 daniel-mar 167
  if Assigned(uz) then uz.Free; // uz ist global, damit AbortDLL aufgerufen werden kann
168
 
1 daniel-mar 169
  uz := TZipMaster19.Create(nil);
170
  try
3 daniel-mar 171
    ExtractionInProcess := true;
1 daniel-mar 172
    {$IFNDEF USE_DZIP_UNPACK}
173
    uz.DLLDirectory := GetTempDirectory + DelZipDLL_Name;
174
    {$ENDIF}
175
    uz.DLL_Load := true;
176
 
177
    uz.ZipFileName := AZipFile;
178
    uz.Active := true;
179
    zb := ReadBehavior(uz.ZipComment);
180
 
181
    uz.Unattended := true;
182
    uz.ExtrOptions := [ExtrDirNames, ExtrOverWrite, ExtrFreshen, ExtrUpdate,
183
      ExtrForceDirs, ExtrNTFS];
184
 
3 daniel-mar 185
    uz.OnExtractOverwrite := EvConfirmOverwrite;
186
    uz.OnProgress := EvProgress;
187
    uz.OnTick := EvTick;
188
    uz.OnCheckTerminate := EvCheckTerminate;
189
    uz.OnPasswordError := EvPasswordEvent;
190
    uz.PasswordReqCount := EvPasswordTries;
191
    uz.OnSkipped := EvSkipEvent;
192
    uz.OnSetExtName := EvExtFNChange;
1 daniel-mar 193
 
2 daniel-mar 194
    // Find out base dirtory
195
 
196
    GeneralBaseDir := '';
197
    case zb.ExtractionTarget of
198
      etExtractHere:
199
        begin
200
          GeneralBaseDir := ExtractFilePath(AZipfile); // Default
201
        end;
202
      etDesktop:
203
        begin
204
          GeneralBaseDir := GetSpecialFolderPath(CSIDL_DESKTOP);
205
        end;
206
      etAsk:
207
        begin
3 daniel-mar 208
          GeneralBaseDir := MySelectDirectory(Lng_SelectDir);
209
          if GeneralBaseDir = '' then Exit;
2 daniel-mar 210
        end;
211
    end;
212
    GeneralBaseDir := IncludeTrailingPathDelimiter(GeneralBaseDir);
213
 
214
    // Semantic scanning of ZIP to determinate the final extraction directory
215
 
1 daniel-mar 216
    l := TStringList.Create;
217
    try
218
      // Count the root objects (files OR dirs) in the ZIP
219
 
220
      ListRootFiles(uz, l);
221
 
222
      if l.Count = 0 then
223
      begin
224
        // Empty ZIP or Extractor.exe was called without ZIP attached
225
        Exit;
226
      end
227
      else if l.Count = 1 then
228
      begin
229
        // 1 Object = Extract it right here!
2 daniel-mar 230
        BaseDir := GeneralBaseDir;
231
        s := BaseDir + l.Strings[0];
232
 
233
        RenamingOldPrefix := l.Strings[0]; // = StripBaseDir(S);
234
 
1 daniel-mar 235
        if zb.ConflictBehavior = cbAvoid then
236
        begin
2 daniel-mar 237
          s := SearchNextFreeName(s, LooksLikeDir(s));
1 daniel-mar 238
        end;
2 daniel-mar 239
 
240
        RenamingNewPrefix := StripBaseDir(s);
1 daniel-mar 241
      end
242
      else
243
      begin
244
        // 2+ Objects = Extract them in a separate folder
2 daniel-mar 245
        s := GeneralBaseDir + ExtractFileNameWithoutExt(AZipfile) + PathDelim;
1 daniel-mar 246
        if zb.ConflictBehavior = cbAvoid then
247
        begin
2 daniel-mar 248
          s := SearchNextFreeName(s, true);
1 daniel-mar 249
          MkDir(s);
250
        end
251
        else
252
        begin
253
          if not DirectoryExists(s) then MkDir(s);
254
        end;
255
        BaseDir := s;
256
      end;
257
      BaseDir := IncludeTrailingPathDelimiter(BaseDir);
258
 
2 daniel-mar 259
      uz.ExtrBaseDir := BaseDir;
1 daniel-mar 260
 
261
      // Pre-Extract-Dialog
262
 
263
      if zb.CommentPresentation = cpBeforeExtracting then
264
      begin
265
        if not CommentForm.ShowCommentModal(uz.ZipComment) then exit;
266
      end;
267
 
268
      // Extract
269
 
270
      ec := uz.Extract;
271
 
272
      if ec <> 0 then
273
      begin
274
        if ec = DS_Canceled then
275
        begin
276
          MessageDlg(Lng_Aborted, mtWarning, [mbOk], 0);
277
        end
278
        else
279
        begin
280
          MessageDlg(Format(Lng_Zip_Error, [uz.ErrMessage, ec]), mtError, [mbOk], 0);
281
        end;
282
      end;
283
 
284
      // Errors?
285
 
286
      if ErrorForm.ErrorsAvailable then
287
      begin
288
        ErrorForm.ShowModal;
289
      end;
290
 
291
      // Show After-Extracting comment?
292
 
293
      if zb.CommentPresentation = cpAfterExtracting then
294
      begin
295
        if not CommentForm.ShowCommentModal(uz.ZipComment) then exit;
296
      end;
297
 
298
      // Now search for an AutoRun.inf
299
 
300
      ar := ExecuteSFXAutoRun(BaseDir);
301
 
302
      if ar.AutoRunSectionAvailable and not ar.ExecutionSucceed then
303
      begin
304
        MessageDlg(Lng_AutoRunFailed, mtError, [mbOk], 0);
305
        ar.OpenUnzippedContent := true;
306
      end;
307
 
308
      // Now open the file for the user
309
 
310
      if not ar.AutoRunSectionAvailable or ar.OpenUnzippedContent then
311
      begin
312
        if DirectoryExists(s) then
313
        begin
314
          // If it is a folder, open it
3 daniel-mar 315
 
316
          ShellExecute(0, 'open', EXPLORER_EXE,
317
            PChar(Format(C_Explorer_Open_Param, [s])), '', SW_NORMAL);
1 daniel-mar 318
        end
319
        else if FileExists(s) then
320
        begin
321
          // If it is a file, then only select it
322
 
323
          // Que: Funktioniert das auch ohne "/n"?
324
          // Im Moment wird bei einem BESTEHENDEN Fenster
325
          // die Selektion nicht durchgeführt.
326
 
3 daniel-mar 327
          ShellExecute(0, 'open', EXPLORER_EXE,
328
            PChar(Format(C_Explorer_Select_Param, [s])), '', SW_NORMAL);
1 daniel-mar 329
        end
330
        else
331
        begin
3 daniel-mar 332
          if not AbortUnzip then
333
          begin
334
            MessageDlg(Lng_Unknown_Error, mtError, [mbOk], 0);
335
          end;
1 daniel-mar 336
        end;
337
      end;
338
    finally
339
      l.Free;
340
    end;
341
  finally
342
    uz.Free;
3 daniel-mar 343
    ExtractionInProcess := false;
1 daniel-mar 344
  end;
345
end;
346
 
3 daniel-mar 347
procedure TMainForm.EvProgress(Sender: TObject; details: TZMProgressDetails);
1 daniel-mar 348
begin
349
  CurrentFileLabel.Caption := details.ItemName;
350
 
351
  progressBar.Position := details.TotalPosition;
352
  progressBar.Max := details.TotalSize;
353
 
354
  itemBar.Position := details.ItemPosition;
355
  itemBar.Max := details.ItemSize;
356
 
357
  Application.ProcessMessages;
358
end;
359
 
3 daniel-mar 360
procedure TMainForm.EvExtFNChange(Sender: TObject;
1 daniel-mar 361
  var FileName: TZMString; const BaseDir: TZMString;
362
  var IsChanged: Boolean);
363
begin
2 daniel-mar 364
  if RenamingOldPrefix = RenamingNewPrefix then Exit;
1 daniel-mar 365
  FileName := RenamingNewPrefix + Copy(FileName, 1+Length(RenamingOldPrefix), Length(FileName)-Length(RenamingOldPrefix));
366
  IsChanged := true;
367
end;
368
 
3 daniel-mar 369
procedure TMainForm.EvTick(Sender: TObject);
1 daniel-mar 370
begin
371
  Application.ProcessMessages;
372
end;
373
 
3 daniel-mar 374
procedure TMainForm.EvCheckTerminate(Sender: TObject; var abort: Boolean);
1 daniel-mar 375
begin
376
  abort := AbortUnzip;
377
end;
378
 
3 daniel-mar 379
procedure TMainForm.EvConfirmOverwrite(Sender: TObject; const ForFile: TZMString;
1 daniel-mar 380
  IsOlder: Boolean; var DoOverwrite: Boolean; DirIndex: Integer);
381
resourcestring
382
  Lng_Overwrite = 'Bestehende Datei "%s" überschreiben?';
383
var
384
  res: Integer;
385
begin
386
  if zb.ConflictBehavior = cbOverwrite then
387
  begin
388
    DoOverwrite := true;
389
    Exit;
390
  end
391
  else if zb.ConflictBehavior = cbNewer then
392
  begin
393
    DoOverwrite := IsOlder; // If file on DISK is older, then overwrite.
394
    Exit;
395
  end
396
  else if zb.ConflictBehavior = cbAsk then
397
  begin
398
    // Workaround: Verzeichnisse brauchen das nicht,
399
    // denn es wird bei den Dateien nochmal nachgefragt
400
    if LooksLikeDir(ForFile) then
401
    begin
402
      DoOverwrite := true;
403
      Exit;
404
    end;
405
 
3 daniel-mar 406
    if CachedOverwriteDecision = odUndefined then
1 daniel-mar 407
    begin
408
      res := MessageDlg(Format(Lng_Overwrite, [ForFile]), mtConfirmation, [mbYes, mbNo, mbYesToAll, mbNoToAll], 0);
409
      DoOverwrite := (res = mrYes) or (res = mrYesToAll);
3 daniel-mar 410
      if res = mrNoToAll then CachedOverwriteDecision := odOverwriteNothing;
411
      if res = mrYesToAll then CachedOverwriteDecision := odOverwriteAll;
1 daniel-mar 412
    end
413
    else
414
    begin
3 daniel-mar 415
      DoOverwrite := CachedOverwriteDecision = odOverwriteAll;
1 daniel-mar 416
    end;
3 daniel-mar 417
  end
418
  else if zb.ConflictBehavior = cbAvoid then
419
  begin
420
    // Nothing to do
1 daniel-mar 421
  end;
422
end;
423
 
3 daniel-mar 424
procedure TMainForm.EvPasswordEvent(Sender: TObject; IsZipAction: Boolean;
1 daniel-mar 425
  var NewPassword: String; const ForFile: TZMString; var RepeatCount: Longword;
426
  var Action: TMsgDlgBtn);
427
var
428
  repc: integer;
429
begin
3 daniel-mar 430
  repc := EvPasswordTries - RepeatCount + 1;
1 daniel-mar 431
 
432
  // Eine Passworteingabe wurde abgebrochen. Frage nicht mehr nach.
433
  if StopAskingPassword then Exit;
434
 
435
  // Wurde schonmal ein Passwort eingegeben?
436
  if LastTriedPassword <> '' then
437
  begin
438
    // Schauen, ob das letzte Passwort auch mit dieser Datei geht.
439
    if repc = 1 then
440
    begin
441
      // Ja, geht
442
      NewPassword := LastTriedPassword;
443
      Exit;
444
    end
445
    else
446
    begin
447
      // Leider nein
448
      LastTriedPassword := '';
449
    end;
450
  end;
451
 
3 daniel-mar 452
  if PasswordDlg.ShowModal(StripBaseDir(ForFile), repc, EvPasswordTries) = mrOk then
1 daniel-mar 453
  begin
454
    NewPassword := PasswordDlg.Password.Text;
455
    if NewPassword = '' then NewPassword := ' '; // Neue Eingabe erzwingen.
456
    LastTriedPassword := NewPassword;
457
  end
458
  else
459
  begin
460
    StopAskingPassword := true;
461
    Action := mbCancel;
462
  end;
463
end;
464
 
465
procedure TMainForm.CancelBtnClick(Sender: TObject);
466
resourcestring
467
  Lng_AbortExtract = 'Extrahieren abbrechen?';
468
begin
3 daniel-mar 469
  if not ExtractionInProcess then
470
  begin
471
    Close;
472
    Exit;
473
  end;
474
 
1 daniel-mar 475
  if MessageDlg(Lng_AbortExtract, mtConfirmation, mbYesNoCancel, 0) = mrYes then
476
  begin
477
    CancelBtn.Enabled := false;
3 daniel-mar 478
    uz.AbortDLL;
1 daniel-mar 479
    AbortUnzip := true;
3 daniel-mar 480
    // Close wird durch den Timer durchgeführt
481
    Exit;
1 daniel-mar 482
  end;
483
end;
484
 
485
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
486
begin
3 daniel-mar 487
  if not ExtractionInProcess then
488
  begin
489
    CanClose := true;
490
    Exit;
491
  end;
492
 
1 daniel-mar 493
  if not AbortUnzip then
494
  begin
3 daniel-mar 495
    CanClose := false;
1 daniel-mar 496
    CancelBtn.Click;
497
  end;
498
end;
499
 
500
procedure TMainForm.FormCreate(Sender: TObject);
501
resourcestring
502
  Lng_Extracting = 'Extrahiere Dateien. Bitte warten...';
503
begin
504
  WaitLabel.Caption := Lng_Extracting;
505
  WaitLabel.Left := progressBar.Width div 2 - WaitLabel.Width div 2;
506
  CurrentFileLabel.Caption := '';
507
end;
508
 
509
{$IFNDEF USE_DZIP_UNPACK}
510
procedure TMainForm.ExtractDllFromResource(ADirectory: string);
511
var
512
  s: TResourceStream;
513
  o: TFileStream;
514
  AOutFile: string;
515
begin
516
  AOutFile := IncludeTrailingPathDelimiter(ADirectory) + DelZipDLL_Name;
517
  s := TResourceStream.Create(0, DZRES_Dll, RT_RCDATA);
518
  try
519
    try
520
      s.Seek(SizeOf(Integer), soFromBeginning); // Ref: ZMDllLoad19.pas:427
521
      if FileExists(AOutFile) then
522
        o := TFileStream.Create(AOutFile, fmOpenWrite or fmShareDenyNone)
523
      else
524
        o := TFileStream.Create(AOutFile, fmCreate or fmShareDenyNone);
525
      try
526
        o.CopyFrom(s, s.Size-s.Position);
527
      finally
528
        o.Free;
529
      end;
530
    except
531
      if FileExists(AOutFile) then
532
      begin
533
        // Probably the file is write-locked (maybe some other Extractor is
534
        // using it right now? Even if we run into danger that the target DLL
535
        // is a write-protected old/incompatible version of DelZip190.dll,
536
        // we do count this as success, since the file exists.
537
      end
538
      else
539
      begin
540
        raise;
541
      end;
542
    end;
543
  finally
544
    s.Free;
545
  end;
546
end;
547
{$ENDIF}
548
 
549
procedure TMainForm.AutoTimerTimer(Sender: TObject);
2 daniel-mar 550
resourcestring
551
  Lng_NakedSFX = 'Das selbstentpackende Archiv (SFX) beschädigt oder ungültig. Wenn Sie diese Datei aus dem Internet bezogen haben, laden Sie sie bitte erneut herunter.';
552
  Lng_FileNotFound = 'Die durch Parameter angegebene Datei "%s" kann nicht gefunden werden!';
553
  Lng_TooManyArguments = 'Zu viele Argumente!';
1 daniel-mar 554
begin
555
  AutoTimer.Enabled := false;
556
 
557
  {$IFNDEF USE_DZIP_UNPACK}
558
  ExtractDllFromResource(GetTempDirectory);
559
  {$ENDIF}
560
 
561
  try
2 daniel-mar 562
    if IsExtractable(ParamStr(0)) then
1 daniel-mar 563
    begin
2 daniel-mar 564
      ExtractZipHere(ParamStr(0));
1 daniel-mar 565
    end
566
    else
567
    begin
2 daniel-mar 568
      // Der Extractor ist "nackt" oder das SFX beschädigt
1 daniel-mar 569
 
2 daniel-mar 570
      if ParamCount = 0 then
571
      begin
572
        MessageDlg(Lng_NakedSFX, mtError, [mbOk], 0);
573
      end
574
      else if ParamCount = 1 then
575
      begin
576
        // In diesem Zustand erlauben wir, fremde SFX zu entpacken (auch für Debugging-Zwecke)
577
        if FileExists(ParamStr(1)) then
578
        begin
579
          ExtractZipHere(ParamStr(1));
580
        end
581
        else
582
        begin
583
          MessageDlg(Lng_FileNotFound, mtError, [mbOk], 0);
584
        end;
585
      end
586
      else if ParamCount = 2 then
587
      begin
588
        // Future: Mehr als nur 1 Parameter erlauben?
589
        MessageDlg(Lng_TooManyArguments, mtError, [mbOk], 0);
590
      end;
1 daniel-mar 591
    end;
592
  finally
593
    Close;
594
  end;
595
end;
596
 
597
function TMainForm.StripBaseDir(const s: string): string;
598
begin
599
  // Warnung: Es wird nicht überprüft, ob der String auch
600
  // wirklich mit dem BaseDir beginnt!
601
  result := Copy(s, Length(BaseDir)+1, Length(s)-Length(BaseDir));
602
end;
603
 
3 daniel-mar 604
procedure TMainForm.EvSkipEvent(Sender: TObject; const ForFile: TZMString;
1 daniel-mar 605
  SkipType: TZMSkipTypes; var ExtError: Integer);
606
resourcestring
607
  Lng_PasswordWrong = 'Das Passwort wurde zu oft falsch eingegeben. Die Datei "%s" wird nicht extrahiert.';
608
begin
609
  if (SkipType = stBadPassword) and not StopAskingPassword then
610
  begin
611
    MessageDlg(Format(Lng_PasswordWrong, [ForFile]), mtError, [mbOk], 0);
612
    LastTriedPassword := '';
613
  end;
614
  ErrorForm.NewError(StripBaseDir(ForFile));
615
end;
616
 
617
end.