My InnoSetup GUI is frozen while unpacking.
I have a procedure DoUnzip(source: String; targetdir: String) with a kernel
unzipTool := ExpandConstant('{tmp}\7za.exe'); Exec(unzipTool, ' x "' + source + '" -o"' + targetdir + '" -y', '', SW_HIDE, ewWaitUntilTerminated, ReturnCode);
This procedure is called several times, and the Exec operation blocks the user interface. There is a very short amount of time between executions where the Inno GUI can be moved / moved.
I know that there are other options for TExecWait instead of ewWaitUntilTerminated , such as ewNoWait and ewWaitUntilIdle , but unfortunately they do not help in this case. Using ewNoWait will result in several unpacking operations being performed simultaneously.
I am looking for a way to perform an external decompression operation and wait for it to complete, but without blocking the user interface. How can I implement this?
Here are my notes and ideas:
Waiting for the completion of the process is blocked if you do not wait in a thread other than the main one. I think some feedback is required that is executed when the unpack operation completes.
I know that InnoSetup does not provide this function out of the box, see https://github.com/jrsoftware/issrc/issues/149
When searching for related questions on StackOverflow, I came across the question of Using a callback to display file names from an external decompression dll (Inno Setup) , where I found Mirals Answer . It uses InnoCallback in combination with another DLL.
I think in my case it could be 7zxa.dll for unzip operation. But he does not accept the callback. Thus, the following code is just a draft concept / idea. One of the problems is that 7zxa.dll does not accept callbacks. Another problem is that the 7zxa API is not really inviting to work.
[Code] type TMyCallback = procedure(Filename: PChar);
Update
@Rik suggested combining the ShellExecuteEx () WinAPI function with INFINITE WaitForSingleObject.
I have implemented and tested this approach. The code is below.
Unpacking works, but the InnoSetup window can only be moved / moved for a short time between separate unlock operations. During a long start, unzip the GUI completely does not respond - drag / drop button / without cancel. I added BringToFrontAndRestore (), but it seems that the new process has focus.
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 ' WaitForSingleObject@kernel32.dll stdcall'; function CloseHandle(hObject: THandle): BOOL; external ' CloseHandle@kernel32.dll 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;