Skip to content

Instantly share code, notes, and snippets.

@reikjarloekl
Forked from jakoch/unzip.iss
Last active December 16, 2021 15:14
Show Gist options
  • Save reikjarloekl/d90a72177cab69ab4aa6b11edc5b21a1 to your computer and use it in GitHub Desktop.
Save reikjarloekl/d90a72177cab69ab4aa6b11edc5b21a1 to your computer and use it in GitHub Desktop.
Helper for executing executables without blocking the InnoSetup GUI
// Based on code written by Rik and Jens A. Koch (@jakoch) on StackOverflow:
// http://stackoverflow.com/questions/32256432/how-to-execute-7zip-without-blocking-the-innosetup-ui
[Code]
#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;
TTickCallBack = procedure;
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';
function GetExitCodeProcess(Process: THandle; var ExitCode: Cardinal): Boolean;
external 'GetExitCodeProcess@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
function ExecNonBlocking(executable: String; parameters: String; tickCallBack: TTickCallBack): Cardinal;
var
ExecInfo: TShellExecuteInfo; // info object for ShellExecuteEx()
ExitCode: Cardinal;
begin
Result := 1;
// executable and parameters might contain {tmp} or {app} constant, so expand/resolve it to path names
executable := ExpandConstant(executable);
parameters := ExpandConstant(parameters);
// prepare information about the application being executed by ShellExecuteEx()
ExecInfo.cbSize := SizeOf(ExecInfo);
ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
ExecInfo.Wnd := 0;
ExecInfo.lpFile := executable;
ExecInfo.lpParameters := parameters;
ExecInfo.nShow := SW_HIDE;
{
The executable is run 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 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
if (tickCallBack <> nil) then tickCallBack();
AppProcessMessage;
WizardForm.Refresh();
end;
GetExitCodeProcess( ExecInfo.hProcess , ExitCode);
Result := ExitCode;
CloseHandle(ExecInfo.hProcess);
end;
end;
@reikjarloekl
Copy link
Author

Compared to the original gist, this returns the exit code and calls an (optional) calback while waiting for the external program to finish.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment