问题描述
|
//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对象)只能对单个线程具有亲和力。主线程。