I'm trying to start another application from my program with elevated rights, and wait for it to terminate before continuing.
I've tried several different solutions on the web, but I can't find one that works exactly right.
The code below is the closest I have to working right. It runs the app with elevated privileges and waits for it to terminate, but it freezes once the external app is terminated. In other words, it doesn't keep processing once the launched app is closed.
How can I accomplish what I'm after here?
procedure TfMain.RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
var
sei: TShellExecuteInfo;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := hWnd;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PChar(aFile);
sei.lpParameters := PChar(aParameters);
sei.nShow := SW_SHOWNORMAL;
if not ShellExecuteEx(@sei) then
RaiseLastOSError
else
while WaitForSingleObject(sei.hProcess, 50) <> WAIT_OBJECT_0 do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
end;
Update:
I've come up with the following function, but it only works if I have a ShowMessage statement after calling it. So, I have to have:
RunFileAsAdminWait(Handle, ExtractFilePath(Application.Exename) + 'AutoUpdate.exe', '/auto');
ShowMessage('test');
in order to make the function work. How can I make it work without the ShowMessage call?
Here's the updated function:
procedure TfMain.RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
var
sei: TShellExecuteInfo;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := hWnd;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PChar(aFile);
sei.lpParameters := PChar(aParameters);
sei.nShow := SW_SHOWNORMAL;
if not ShellExecuteEx(@sei) then
RaiseLastOSError
else
if sei.hProcess <> 0 then
WaitForSingleObject(sei.hProcess, 50)
else
Exit;
CloseHandle(sei.hProcess);
end;
The following code works for me:
procedure RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
var
sei: TShellExecuteInfo;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := hWnd;
sei.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := 'runas';
sei.lpFile := PChar(aFile);
sei.lpParameters := PChar(aParameters);
sei.nShow := SW_SHOWNORMAL;
if not ShellExecuteEx(@sei) then
RaiseLastOSError;
if sei.hProcess <> 0 then begin
while WaitForSingleObject(sei.hProcess, 50) = WAIT_TIMEOUT do
Application.ProcessMessages;
CloseHandle(sei.hProcess);
end;
end;
You have to pass the SEE_MASK_NOCLOSEPROCESS
flag to get the process handle to wait for. I also changed the code to loop as long as WaitForSingleObject()
returns with timeout.
For more information on the flags see the MSDN page for the SHELLEXECUTEINFO
structure.
The code in @mghie's answer has the right idea in general, but the code to process messages while waiting on the process handle could be better. Try this:
procedure RunFileAsAdminWait(hWnd: HWND; aFile, aParameters: string);
var
sei: TShellExecuteInfo;
Ret: DWORD;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := hWnd;
sei.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb := 'runas';
sei.lpFile := PChar(aFile);
sei.lpParameters := PChar(aParameters);
sei.nShow := SW_SHOWNORMAL;
if not ShellExecuteEx(@sei) then
RaiseLastOSError;
if sei.hProcess <> 0 then
try
repeat
Ret := MsgWaitForMultipleObjects(1, sei.hProcess, False, INFINITE, QS_ALLINPUT);
if Ret = (WAIT_OBJECT_0+1) then Application.ProcessMessages
else if Ret = WAIT_FAILED then RaiseLastOSError;
until Ret = WAIT_OBJECT_0;
finally
CloseHandle(sei.hProcess);
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With