我的InnoSetup GUI在解压缩操作过程中被冻结。如何在不阻止InnoSetup UI的情况下执行7zip?
我有个procedure DoUnzip(source: String; targetdir: String)
与芯
unzipTool := ExpandConstant('{tmp}\7za.exe');
Exec(unzipTool, ' x "' + source + '" -o"' + targetdir + '" -y',
'', SW_HIDE, ewWaitUntilTerminated, ReturnCode);
该过程被称为多次和Exec
操作块中的用户界面。执行之间只有很短的时间,Inno GUI可拖动/移动。
我知道有TExecWait
而不是ewWaitUntilTerminated
的其他选项,如ewNoWait
和ewWaitUntilIdle
,但不幸的是他们在这种情况下没有帮助。使用ewNoWait
将导致同时执行多个解压缩操作。
我正在寻找一种方法来执行外部解压缩操作并等待它完成,但不会阻塞用户界面。我怎样才能实现呢?
这里是我的笔记和想法:
等待一个过程完成,阻止,除非你从主一个不同的线程等待。我认为需要执行一些回调,当解压缩操作完成时。
我知道,InnoSetup不提供此功能的开箱,见https://github.com/jrsoftware/issrc/issues/149
当搜索StackOverflow上相关的问题,我想出了这个问题Using callback to display filenames from external decompression dll (Inno Setup),在那里我发现Mirals's answer。它将InnoCallback与另一个DLL结合使用。
我认为,在我的情况下,这可能是7zxa.dll
的解压缩操作。但它不接受回调。所以,下面的代码只是一个概念/思想草案。一个问题是,7zxa.dll
不接受回调。 另一个问题是7zxa API并没有真正邀请使用。
[Code]
type
TMyCallback = procedure(Filename: PChar);
// wrapper to tell callback function to InnoCallback
function WrapMyCallback(Callback: TMyCallback; ParamCount: Integer): LongWord;
external '[email protected]:innocallback.dll stdcall';
// the call to the unzip dll
// P!: the 7zxa.dll doesn't accept a callback
procedure DoUnzipDll(Blah: Integer; Foo: String; ...; Callback: LongWord);
external '[email protected]:7zxa.dll stdcall';
// the actual callback action
procedure MyCallback(Filename: PChar);
begin
// refresh the GUI
end;
//-----
var Callback : LongWord;
// tell innocallback the callback procedure as 1 parameter
Callback := WrapMyCallback(@MyCallback, 1);
// pass the wrapped callback to the unzip DLL
DoUnzipDll(source, target, ..., Callback);
procedure DoUnzip(src, target : String);
begin
DoUnzipDll(ExpandConstant(src), ExpandConstant(target));
end;
更新
@Rik建议的WinAPI的函数的ShellExecuteEx()与WaitForSingleObject的INFINITE结合。
我已经实现并测试了这种方法。代码如下。
解压缩的工作,但InnoSetup窗口只能在单个解压缩操作之间短时间移动/拖动。在长时间运行解压缩期间,GUI完全无响应 - 无拖动/不取消按钮。 我已经添加了BringToFrontAndRestore(),但它似乎是新进程的焦点。
const
WAIT_OBJECT_0 = $0;
WAIT_TIMEOUT = $00000102;
SEE_MASK_NOCLOSEPROCESS = $00000040;
INFINITE = $FFFFFFFF; { Infinite timeout }
type
TShellExecuteInfo = record
cbSize: DWORD;
fMask: Cardinal;
Wnd: HWND;
lpVerb: string;
lpFile: string;
lpParameters: string;
lpDirectory: string;
nShow: Integer;
hInstApp: THandle;
lpIDList: DWORD;
lpClass: string;
hkeyClass: THandle;
dwHotKey: DWORD;
hMonitor: THandle;
hProcess: THandle;
end;
function ShellExecuteEx(var lpExecInfo: TShellExecuteInfo): BOOL;
external 'ShellExecuteEx{#AW}@shell32.dll stdcall';
function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD): DWORD;
external '[email protected] stdcall';
function CloseHandle(hObject: THandle): BOOL; external '[email protected] stdcall';
procedure DoUnzip(source: String; targetdir: String);
var
unzipTool, unzipParams : String; // path to unzip util
ReturnCode : Integer; // errorcode
ExecInfo: TShellExecuteInfo;
begin
// source might contain {tmp} or {app} constant, so expand/resolve it to path name
source := ExpandConstant(source);
unzipTool := ExpandConstant('{tmp}\7za.exe');
unzipParams := ' x "' + source + '" -o"' + targetdir + '" -y';
ExecInfo.cbSize := SizeOf(ExecInfo);
ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
ExecInfo.Wnd := 0;
ExecInfo.lpFile := unzipTool;
ExecInfo.lpParameters := unzipParams;
ExecInfo.nShow := SW_HIDE;
if not FileExists(unzipTool)
then MsgBox('UnzipTool not found: ' + unzipTool, mbError, MB_OK)
else if not FileExists(source)
then MsgBox('File was not found while trying to unzip: ' + source, mbError, MB_OK)
else begin
// ShellExecuteEx combined with INFINITE WaitForSingleObject
if ShellExecuteEx(ExecInfo) then
begin
while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_OBJECT_0
do begin
InstallPage.Surface.Update;
//BringToFrontAndRestore;
WizardForm.Refresh();
end;
CloseHandle(ExecInfo.hProcess);
end;
end;
end;
我可能是大错特错这里,但不能你只需要使用'shellexecuteex'和'与无限超时WaitForSingleObject'?如果它仍然阻塞在INFINITE超时时间内,则可以使用较小的超时时间并循环,直到过程完成。请参阅[答案](http://stackoverflow.com/a/10910780/1037511)。 – Rik
这是一个有趣的想法。谢谢!我会测试这种方法并报告回来。 –
@Rik我已经实现了你的想法,并将它的代码添加到我的问题中。只有在多个解压缩操作之间的短时间内,该窗口才会响应。在长时间运行解压缩期间,Inno GUI仍处于冻结状态。 –