Skip to content

Instantly share code, notes, and snippets.

@jakoch
Created August 28, 2015 10:34
Show Gist options
  • Save jakoch/33ac13800c17eddb2dd4 to your computer and use it in GitHub Desktop.
Save jakoch/33ac13800c17eddb2dd4 to your computer and use it in GitHub Desktop.
Unzip Helper for executing 7zip without blocking the InnoSetup GUI
{
Unzip Helper for executing 7zip without blocking the InnoSetup GUI
----
The main procedure is the non-blocking Unzip().
Your GUI will remain responsive during the unzip operation.
Written 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\unzip.iss"
2. Add the unzip tool "7za.exe" to the [Files] section of your installer
and copy it to the temp folder during installation.
// [Files]
// Source: ..\some\where\7za.exe; DestDir: {tmp}; Flags: dontcopy
3. Finally, extract your files using Unzip(source, target); 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 '[email protected] stdcall';
function CloseHandle(hObject: THandle): BOOL; external '[email protected] 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 '[email protected] stdcall';
function TranslateMessage(const lpMsg: TMsg): BOOL; external '[email protected] stdcall';
function DispatchMessage(const lpMsg: TMsg): Longint; external '[email protected] 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 Unzip(source: String; targetdir: String);
var
unzipTool, unzipParams : String; // path and param for the unzip tool
ExecInfo: TShellExecuteInfo; // info object for ShellExecuteEx()
begin
// source and targetdir might contain {tmp} or {app} constant, so expand/resolve it to path names
source := ExpandConstant(source);
targetdir := ExpandConstant(targetdir);
// prepare 7zip execution
unzipTool := ExpandConstant('{tmp}\7za.exe');
unzipParams := ' x "' + source + '" -o"' + targetdir + '" -y';
// prepare information about the application being executed by ShellExecuteEx()
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
{
The unzip tool 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;
end;
@jenokizm
Copy link

After include, I have an error at the compilation stage:

[Content]
File: C:\Program Files (x86)\Inno Setup 5\unzip.iss
Line 1:
Missing "=" separator between message name and text.

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