I have a function which allows me to launch a sub-process, wait until it's terminated, and return an exit code. The sub-process is Windows Installer which installs an MSI package (showing just the current progress of its install). This function is called from within a background thread.
I've wrapped this function like so:
function ShellExec(const Verb, Cmd, Params, Dir: WideString;
const Visible: Bool): Integer;
var
FN: String;
SEInfo: TShellExecuteInfo;
ExitCode: DWORD;
begin
Result:= 1;
FillChar(SEInfo, SizeOf(SEInfo), 0);
SEInfo.cbSize := SizeOf(TShellExecuteInfo);
with SEInfo do begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpFile := PChar(Cmd);
lpParameters := PChar(Params);
lpDirectory := PChar(Dir);
if Visible then
nShow := SW_SHOWNORMAL
else
nShow := SW_HIDE;
end;
if ShellExecuteEx(@SEInfo) then begin
repeat
GetExitCodeProcess(SEInfo.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE) or
Application.Terminated;
end else begin
ExitCode:= 1;
end;
Result:= ExitCode;
end;
And then I use it like this:
function InstallNodeJs(const Config: TInstConfig): Integer;
var
FN: String;
Params: string;
begin
Result:= 1;
if Config.Is64 then
FN := 'NodeInstall_x64.msi'
else
FN := 'NodeInstall_x86.msi';
FN:= Config.TmpDir + FN;
Params:= '/i "'+FN+'" /norestart /passive';
Result:= ShellExec('', 'msiexec.exe', Params, '', True);
end;
This works fine, however it does not suspend input from my calling app - user is still able to click on my application, despite the loop in the above function - and then my app goes on top of the sub-process. I need the sub-process to stay on top of my application.
The process is launched from within a worker thread behind the main form. The thread uses events to notify the main form of its progress. The main form has a couple animations in which I still want to continue, but I don't want the user to be able to click back on the app while this sub-app is still running. I'd like it to appear like a modal state. Essentially the MSI progress should stay on top of my application at all times.
How can I run this sub-process and prevent the user from going back to the main app and prevent the main app from going on top of the sub-app? This is the default behavior when doing the same thing from Inno Setup, but I can't figure out how to do it in a Delphi application.