Multithreading Delphi:ZipForge in thread=";画布不允许绘制“;所有的形式都被冻结了

Multithreading Delphi:ZipForge in thread=";画布不允许绘制“;所有的形式都被冻结了,multithreading,delphi,unzip,Multithreading,Delphi,Unzip,如何在没有冻结表单和EINValidoOperation错误的情况下解压缩?谢谢 解压;//在ZipForge1Password上,我收到错误:EINValidoOperation,消息为“Canvas不允许绘制”。提取存档文件时,表单不会冻结。 这是因为您从线程中运行线程不安全代码(1)。所有线程不安全代码(1)(与大多数VCL和WinAPI例程一样)都必须在主线程中运行。解压例程引用form2,这是一个VCL组件,因此您必须(1)使用Synchronize将执行临时转移到主线程 同步(解压缩

如何在没有冻结表单和EINValidoOperation错误的情况下解压缩?谢谢

解压;//在ZipForge1Password上,我收到错误:EINValidoOperation,消息为“Canvas不允许绘制”。提取存档文件时,表单不会冻结。

这是因为您从线程中运行线程不安全代码(1)。所有线程不安全代码(1)(与大多数VCL和WinAPI例程一样)都必须在主线程中运行。解压例程引用form2,这是一个VCL组件,因此您必须(1)使用Synchronize将执行临时转移到主线程

同步(解压缩);//ZipForge1Password上没有EINValidoOperation错误,但在提取存档文件时表单被冻结。

如前所述,使用Synchronize时,您故意在主线程中执行代码,因此在提取过程中代码似乎被冻结

所以现在你面临着鸡和蛋的两难选择。一个可以通过在线程中创建ZipForge组件@runtime来消除的问题。在这种情况下,唯一保持同步的用户交互就是提供密码。缺点是同步只需要一个无参数的方法,所以您需要做一些工作来实现OnPassword事件处理程序。它看起来如下所示:

//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;
免责声明:我不熟悉ZipForge组件,因此此代码可能不完整。您应该在TUnZip.Execute中实现所有designtime生成的设置。我甚至不知道TZipForge是否有OnPassword事件,但我是根据您的代码编写的

编辑:

(1) 并不是所有的VCL代码都是线程不安全的,也不是说从第二个线程到主线程控件、组件或变量的每次调用都是危险的,但防止它是一种很好的做法。对我来说,线程安全这一术语非常谨慎。但这种不安全的实际原因是所有用户界面控件(即所有windows GDI对象)只能与单个线程关联;主线

解压;//在ZipForge1Password上,我收到错误:EINValidoOperation,消息为“Canvas不允许绘制”。提取存档文件时,表单不会冻结。

这是因为您从线程中运行线程不安全代码(1)。所有线程不安全代码(1)(与大多数VCL和WinAPI例程一样)都必须在主线程中运行。解压例程引用form2,这是一个VCL组件,因此您必须(1)使用Synchronize将执行临时转移到主线程

同步(解压缩);//ZipForge1Password上没有EINValidoOperation错误,但在提取存档文件时表单被冻结。

如前所述,使用Synchronize时,您故意在主线程中执行代码,因此在提取过程中代码似乎被冻结

所以现在你面临着鸡和蛋的两难选择。一个可以通过在线程中创建ZipForge组件@runtime来消除的问题。在这种情况下,唯一保持同步的用户交互就是提供密码。缺点是同步只需要一个无参数的方法,所以您需要做一些工作来实现OnPassword事件处理程序。它看起来如下所示:

//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;
免责声明:我不熟悉ZipForge组件,因此此代码可能不完整。您应该在TUnZip.Execute中实现所有designtime生成的设置。我甚至不知道TZipForge是否有OnPassword事件,但我是根据您的代码编写的

编辑:


(1) 并不是所有的VCL代码都是线程不安全的,也不是说从第二个线程到主线程控件、组件或变量的每次调用都是危险的,但防止它是一种很好的做法。对我来说,线程安全这一术语非常谨慎。但这种不安全的实际原因是所有用户界面控件(即所有windows GDI对象)只能与单个线程关联;主线程。

@NGLN现在,如果这是公认的答案,请您更正事实错误。问题不在于缺乏线程安全性,而在于UI控件的线程亲和力。评估表单对象的字段很好,问题是显示模式输入对话框。@David是的,我知道您可以访问表单、控件等,只要您正在阅读。但是为了确保永远不会触及任何棘手的getter或WinAPI句柄或-资源,我总是尽量避免触及VCL,从而根本不触及任何主线程单元。事实仍然是,VCL不是线程安全的。特别是,从TControl派生的所有内容在secundary线程中都是一个潜在的噩梦,不道德的使用可能会导致竞速条件、死锁、组件引用的覆盖、句柄的引用计数无效、数据和共享变量的损坏等等。[/rant]好的,我现在删除了我的答案,因为没有必要让两个人说大致相同的话。@NGLN现在,如果这是公认的答案,请你纠正事实上的错误。问题不在于缺乏线程安全性,而在于UI控件的线程亲和力。评估表单对象的字段很好,问题是显示模式输入对话框。@David是的,我知道您可以访问表单、控件等,只要您正在阅读。但是为了确保永远不会触及任何棘手的getter或WinAPI句柄或-资源,我总是尽量避免触及VCL,从而根本不触及任何主线程单元。事实仍然是,VCL不是线程安全的。特别是,从TControl派生的所有内容在secundary线程中都是一个潜在的噩梦,不道德的使用可能会导致竞速条件、死锁、组件引用的覆盖、句柄的引用计数无效、数据和共享变量的损坏等等。好的,我已经删除了我的answ