diff options
Diffstat (limited to 'installer/non-ui-blocking-exec.iss')
| -rw-r--r-- | installer/non-ui-blocking-exec.iss | 138 | 
1 files changed, 138 insertions, 0 deletions
| diff --git a/installer/non-ui-blocking-exec.iss b/installer/non-ui-blocking-exec.iss new file mode 100644 index 00000000..6eec259d --- /dev/null +++ b/installer/non-ui-blocking-exec.iss @@ -0,0 +1,138 @@ +[Code] +{ +	Exec Helper for executing commands without blocking the InnoSetup GUI + +    ---- + +	The main procedure is NonUiBlockingExec(). +	Your GUI will remain responsive during the operation. + +    Initially written for 7zip by Rik and Jens A. Koch (@jakoch) on StackOverflow: +    http://stackoverflow.com/questions/32256432/how-to-execute-7zip-without-blocking-the-innosetup-ui + +    ---- + +    Usage: + +	1. Include this ISS with + +	   // #include "..\some\where\non-ui-blocking-exec.iss" + +	2. extract your files using NonUiBlockingExec(command, params); in the [Code] section. +} + +#IFDEF UNICODE +  #DEFINE AW "W" +#ELSE +  #DEFINE AW "A" +#ENDIF + +// --- Start "ShellExecuteEx" Helper + +const +  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'; + +// --- End "ShellExecuteEx" Helper + +// --- Start "Application.ProcessMessage" Helper +{ +   InnoSetup does not provide Application.ProcessMessage(). +   This is "generic" code to recreate a "Application.ProcessMessages"-ish procedure, +   using the WinAPI function PeekMessage(), TranslateMessage() and DispatchMessage(). +} +type +  TMsg = record +    hwnd: HWND; +    message: UINT; +    wParam: Longint; +    lParam: Longint; +    time: DWORD; +    pt: TPoint; +  end; + +const +  PM_REMOVE = 1; + +function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageA@user32.dll stdcall'; +function TranslateMessage(const lpMsg: TMsg): BOOL; external 'TranslateMessage@user32.dll stdcall'; +function DispatchMessage(const lpMsg: TMsg): Longint; external 'DispatchMessageA@user32.dll stdcall'; + +procedure AppProcessMessage; +var +  Msg: TMsg; +begin +  while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do begin +    TranslateMessage(Msg); +    DispatchMessage(Msg); +  end; +end; + +// --- End "Application.ProcessMessage" Helper + +procedure NonUiBlockingExec(command: String; params: String); +var +  ExecInfo: TShellExecuteInfo;     // info object for ShellExecuteEx() +begin +    // source and targetdir might contain {tmp} or {app} constant, so expand/resolve it to path names +    command := ExpandConstant(command); +    params := ExpandConstant(params); + +    // prepare information about the application being executed by ShellExecuteEx() +    ExecInfo.cbSize := SizeOf(ExecInfo); +    ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS; +    ExecInfo.Wnd := 0; +    ExecInfo.lpFile := command; +    ExecInfo.lpParameters := params; +    ExecInfo.nShow := SW_HIDE; + +	{ +	 The executable is executed via ShellExecuteEx() +	 Then the installer uses a while loop with the condition +	 WaitForSingleObject and a very minimal timeout +	 to execute AppProcessMessage. + +	 AppProcessMessage is itself a helper function, because +	 Innosetup does not provide Application.ProcessMessages(). +	 Its job is to be the message pump to the InnoSetup GUI. + +	 This trick makes the window responsive/dragable again, +	 while the extraction is done in the background. +	} + +	if ShellExecuteEx(ExecInfo) then +	begin +		while WaitForSingleObject(ExecInfo.hProcess, 100) = WAIT_TIMEOUT +		do begin +			AppProcessMessage; +			WizardForm.Refresh(); +		end; +		CloseHandle(ExecInfo.hProcess); +	end; +end;
\ No newline at end of file | 
