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;