Delphi: ZipForge in thread = "Холст не позволяет рисовать" Форма заморожена - PullRequest
1 голос
/ 13 июня 2011
//Thread
Procedure StartUpdating.UnZip;
begin
form2.ZipForge1.FileName := ItemToExtract;
form2.ZipForge1.OpenArchive;
form2.ZipForge1.BaseDir := XXX;
form2.ZipForge1.ExtractFiles('*.*');
form2.ZipForge1.CloseArchive;
end;

PROCEDURE StartUpdating.Execute;
begin
UnZip; // on ZipForge1Password I get error: EInvalidOperation with message 'Canvas does not allow drawing'. The Form is not frozen while archive is being extracted.
Synchronize(UnZip); //  No EInvalidOperation error on ZipForge1Password, but the Form is frozen while archive is being extracted.
end;

procedure TForm2.ZipForge1Password(Sender: TObject; FileName: string;
  var NewPassword: AnsiString; var SkipFile: Boolean);
var  s:string;
begin

if PassSkip then SkipFile:=true else
    begin
    if InputQuery('Pass',FileName, s) then NewPassword:=ansistring(s) else //I suppose EInvalidOperation error is here
        begin
          PassSkip:=true;
          SkipFile:=true;
          ThreadUpdating.Terminate;
        end;
    end;
end;

Как разархивировать без застывшей формы и без ошибки EInvalidOperation?Спасибо!

1 Ответ

4 голосов
/ 14 июня 2011

UnZip; // on ZipForge1Password I get error: EInvalidOperation with message 'Canvas does not allow drawing'. The Form is not frozen while archive is being extracted.

Это потому, что вы запускаете небезопасный код потока (1) из вашего потока. Весь небезопасный код потока (1) (как и большинство подпрограмм VCL и WinAPI) должен выполняться в основном потоке. Подпрограмма UnZip ссылается на form2, которая является компонентом VCL, поэтому вы должны (1) использовать Synchronize для временной передачи выполнения в основной поток.

Synchronize(UnZip); // No EInvalidOperation error on ZipForge1Password, but the Form is frozen while archive is being extracted.

Как объяснено, с помощью Synchronize вы сознательно выполняете код в главном потоке, поэтому он кажется замороженным во время процесса извлечения.

Так что теперь у вас есть небольшая дилемма курица-яйцо. Тот, который может быть устранен путем создания компонента ZrForge @runtime в вашей теме. В этом случае единственное взаимодействие с пользователем, которое остается синхронизированным, - это предоставление пароля. Недостатком является то, что Synchronize принимает только метод без параметров, поэтому вам нужно проделать небольшую работу для реализации обработчика события OnPassword. Это может выглядеть следующим образом:

type
  TUnZip = class(TThread)
  private
    FFileName: String;
    FPassword: AnsiString;
    FSkipFile: Boolean;
    procedure DoPassword;
    procedure ZipForgePassword(Sender: TObject; FileName: string;
      var NewPassword: AnsiString; var SkipFile: Boolean);
  protected
    procedure Execute; override;
  public
    property PassSkip ...
    property ItemToExtract ...
  end;

{ TUnZip }

procedure TUnZip.DoPassword;
var
  S: String;
begin
  if PassSkip then
    FSkipFile := True
  else if InputQuery('Pass', FFileName, S) then
    FPassword := AnsiString(S)
  else
  begin
    PassSkip := True;
    FSkipFile := True;
    Terminate;
  end;
end;

procedure TUnZip.Execute;
var
  ZipForge: TZipForge;
begin
  ZipForge := TZipForge.Create(...);
  try
    ZipForge.OnPassword := ZipForgePassword;
    ZipForge.FileName := ItemToExtract;
    ZipForge.OpenArchive;
    if not Terminated then  {Assuming OpenArchive triggers the OnPassword event}
    begin
      ZipForge.BaseDir := XXX;
      ZipForge.ExtractFiles('*.*');
      ZipForge.CloseArchive;
    end;
  finally
    ZipForge.Free;
  end;
end;

procedure TUnZip.ZipForgePassword(Sender: TObject; FileName: String;
  var NewPassword: AnsiString; var SkipFile: Boolean);
begin
  FFileName := FileName;
  FPassword := NewPassword;
  FSkipFile := SkipFile;
  Synchronize(DoPassword);
  FileName := FFileName;
  NewPassword := FPassword;
  SkipFile := FSkipFile;
end;

Отказ от ответственности: я не знаком с компонентом ZipForge, поэтому этот код может быть неполным. Вы должны реализовать все параметры, сгенерированные во время разработки, в TUnZip.Execute. Я даже не знаю, есть ли у TZipForge событие OnPassword, но я сделал это из вашего кода.

Edit:

(1) Не весь код VCL небезопасен для потока, и не говорится, что каждый вызов из вспомогательного потока в элемент управления, компонент или переменную основного потока опасен, но это хорошая практика, чтобы предотвратить это. Для меня термин безопасность потока удобно предостерегает. Но действительная причина этой небезопасности заключается в том, что все элементы управления пользовательского интерфейса (то есть все объекты GDI Windows) могут иметь сходство только с одним потоком; основная нить.

...