Created
August 28, 2015 10:34
-
-
Save jakoch/33ac13800c17eddb2dd4 to your computer and use it in GitHub Desktop.
Unzip Helper for executing 7zip without blocking the InnoSetup GUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
After include, I have an error at the compilation stage: