1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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;
|