Delphi: Как запустить приложение с повышенным статусом и дождаться его завершения?
Я пытаюсь запустить другое приложение из моей программы с повышенными правами и дождаться его завершения до продолжения.
Я пробовал несколько разных решений в Интернете, но я не могу найти тот, который работает точно.
Ниже приведен код ниже, я должен работать правильно. Он запускает приложение с повышенными привилегиями и ждет его завершения, но он зависает после завершения внешнего приложения. Другими словами, он не будет продолжать обработку после закрытия приложения.
Как я могу выполнить то, что я здесь сделаю?
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:
У меня появилась следующая функция, но она работает, только если у меня есть оператор ShowMessage после ее вызова. Итак, я должен иметь:
RunFileAsAdminWait(Handle, ExtractFilePath(Application.Exename) + 'AutoUpdate.exe', '/auto');
ShowMessage('test');
чтобы заставить функцию работать. Как я могу заставить его работать без вызова ShowMessage?
Здесь обновленная функция:
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;
Ответы
Ответ 1
Следующий код работает для меня:
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;
Вы должны передать флаг SEE_MASK_NOCLOSEPROCESS
, чтобы обработать дескриптор процесса. Я также изменил код на цикл, пока WaitForSingleObject()
возвращается с таймаутом.
Дополнительные сведения о флажках см. на странице MSDN для структуры SHELLEXECUTEINFO
.
Ответ 2
Код в ответе @mghie имеет правильную идею в целом, но код для обработки сообщений в ожидании на дескрипторе процесса может быть лучше. Попробуйте следующее:
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;
Ответ 3
Ваше ожидание (50 мс слишком короткое), попробуйте
WaitForSingleObject(sei.hProcess, INFINITE)
Проверка правильного дескриптора процесса (sei.hProcess < > 0) может быть опущена.
Исправленный ответ:
while MsgWaitForMultipleObjects(1, sei.hProcess, False, INFINITE, QS_ALLINPUT)
<> WAIT_OBJECT_0 do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
begin
DispatchMessage(Msg);
end;
end;