Delphi:冻结在Form =“画布不允许绘制”的线程中的ZipForge

问题描述

|
//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错误的情况下解压缩?谢谢!     

解决方法

           
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例程引用作为VCL组件的form2,因此您必须(1)使用Synchronize临时将执行转移到主线程。   
Synchronize(UnZip); //  No EInvalidOperation error on ZipForge1Password,but the Form is frozen while archive is being extracted.
如前所述,使用Synchronize可以在主线程中有意执行代码,因此在提取过程中似乎被冻结了。 因此,现在您遇到了一些小问题。可以通过在线程中创建ZipForge组件@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事件,但是我是从您的代码中弥补的。 编辑: (1)并非VCL的所有代码都是线程不安全的,并且并不是说从次线程中到主线程控件,组件或变量的每次调用都是危险的,但是最好的做法是防止这种情况。对我来说,线程安全一词是个谨慎的警告。但是,造成这种不安全的实际原因是,所有用户界面控件(即所有Windows GDI对象)只能对单个线程具有亲和力。主线程。     

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...