//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错误的情况下解压缩?谢谢!
答案 0 :(得分:4)
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,你故意在主线程中执行代码,因此它似乎在提取过程中被冻结。
所以现在你有一点鸡和蛋的困境。可以通过在线程中创建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的所有代码都是线程不安全的,并且不是说从一个secundary线程内到主线程控件,组件或变量的每次调用都是危险的,但它是防止它的好习惯。对我来说,术语线程安全是非常谨慎的。但是这个不安全的实际原因是所有用户界面控件(即所有窗口GDI对象)只能与单个线程具有亲和力;主线。