Как правильно использовать метод WaitForSingleObject для ожидания завершения внешней программы?

Я пытаюсь запустить внешнее приложение с повышенным статусом и дождаться его выхода, прежде чем продолжить:

var
  FProcess: THandle;
  ExecInfo: TShellExecuteInfo;
begin

  FillChar(ExecInfo, SizeOf(ExecInfo), 0);
  with ExecInfo do
  begin
    cbSize := SizeOf(ExecInfo);
    fMask := 0;
    Wnd := AWindow;
    lpVerb := 'runas';
    lpFile := PChar(APath);
    lpParameters := PChar(AParams);
    lpDirectory := PChar(AWorkDir);
    nShow := SW_NORMAL;
  end;

  Result := ShellExecuteEx(@ExecInfo);

  if Wait then
  begin
    while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_TIMEOUT do
      Application.ProcessMessages;
  end;

это запускается, но он просто продолжает ждать. Вызывающая программа никогда не продолжает вызов WaitForSingleObject, даже после выхода вызываемой программы.

Я пробовал WAIT_OBJECT_0 вместо WAIT_TIMEOUT, но у меня такая же проблема. Что я здесь делаю не так?

3 ответов


код

while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_TIMEOUT do
  Application.ProcessMessages;

- Это должен делать? Это бесконечная петля.

использовать просто

WaitForSingleObject(ExecInfo.hProcess, INFINITE);
. И да, вам нужно
fMask:= SEE_MASK_NOCLOSEPROCESS;

для получения дескриптора процесса.


ваш код сломан. Вы не проходите SEE_MASK_NOCLOSEPROCESS флаг ShellExecuteEx(), поэтому он не вернет вам допустимый дескриптор процесса, и ваш цикл игнорирует ошибки, которые WaitForSingleObject() говорит вам, так что вы в конечном итоге в бесконечный цикл.

попробуйте это вместо этого:

var
  ExecInfo: TShellExecuteInfo;
begin
  ZeroMemory(@ExecInfo, SizeOf(ExecInfo));
  with ExecInfo do
  begin
    cbSize := SizeOf(ExecInfo);
    fMask := SEE_MASK_NOCLOSEPROCESS;
    Wnd := AWindow;
    lpVerb := 'runas';
    lpFile := PChar(APath);
    lpParameters := PChar(AParams);
    lpDirectory := PChar(AWorkDir);
    nShow := SW_NORMAL;
  end;
  Result := ShellExecuteEx(@ExecInfo);
  if Result and Wait then
  begin
    if ExecInfo.hProcess <> 0 then // no handle if the process was activated by DDE
    begin
      repeat
        if MsgWaitForMultipleObjects(1, ExecInfo.hProcess, FALSE, INFINITE, QS_ALLINPUT) = (WAIT_OBJECT_0+1) then
          Application.ProcessMessages
        else
          Break;
      until False;
      CloseHandle(ExecInfo.hProcess);
    end;
  end; 
end;

Если вы читали описание ShellExecuteEx в MSDN, вы увидите это:

hProcess

Type: HANDLE

дескриптор недавно запущенного приложения. Этот элемент установлен на возвращает и всегда NULL, если fMask установлено SEE_MASK_NOCLOSEPROCESS. Даже если fMask имеет значение SEE_MASK_NOCLOSEPROCESS, hProcess будет значение NULL, если процесс не запущен.

т. е. у вас просто нет дескриптора. Вам нужно установить fMask как написано выше.