Как правильно использовать метод WaitForSingleObject, чтобы дождаться завершения внешней программы? - PullRequest
9 голосов
/ 10 марта 2011

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

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 ]

12 голосов
/ 10 марта 2011

Что должен делать код

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

?Это бесконечный цикл.

Используйте вместо него

WaitForSingleObject(ExecInfo.hProcess, INFINITE);

.И да, вам нужно

fMask:= SEE_MASK_NOCLOSEPROCESS;

, чтобы получить дескриптор процесса.

4 голосов
/ 11 марта 2011

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

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

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;
2 голосов
/ 10 марта 2011

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

hProcess

Type: HANDLE

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

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

...