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) могут иметь сходство только с одним потоком; основная нить.