Процесс не будет ждать? - PullRequest
0 голосов
/ 20 мая 2019

код, который я использую, как показано ниже:

 begin
     saSecurity.nLength := SizeOf(TSecurityAttributes);
     saSecurity.bInheritHandle := True;
     saSecurity.lpSecurityDescriptor := nil;
     FillChar(suiStartup, SizeOf(TStartupInfo), #0);
     suiStartup.cb := SizeOf(TStartupInfo);
     suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
     suiStartup.wShowWindow := SW_HIDE;

     ccOk:=CreateProcess(nil, PChar(ExecutableFirst+' '+CommandsFirst),@saSecurity,@saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess);

      if ccOk then
       begin
         CreateProcess(nil, PChar(ExecutableSecond + ' ' + CommandsSecond), @saSecurity,@saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess);
           try
            repeat                 Running:=MsgWaitForMultipleObjects(2,piProcess.hProcess,True,100,QS_ALLINPUT);
             Application.ProcessMessages;
             until Running <> WAIT_TIMEOUT
           finally
            CloseHandle(piProcess.hProcess);
            CloseHandle(piProcess.hThread);
            if (Running=WAIT_OBJECT_0) then BidsConversion; //run this when both process has finished
           end;
       end else    
       begin
       raise.Exception(GetLastError.toString);
        Exit
       end;
 end; 

Код работает, но иногда он запускается BidsConversion, однако Первый процесс все еще не завершен, поэтому возникло исключение.

Почему приложение не ожидает завершения обоих процессов, а затем запускает процедуру?

1 Ответ

2 голосов
/ 20 мая 2019

Вы не проверяете возвращаемое значение 2-го CreateProcess(), чтобы увидеть, не сработало ли оно, но, что более важно, вы полностью неправильно используете MsgWaitForMultipleObjects():

  • вы не передаете оба процесса обрабатывают до MsgWaitForMultipleObjects(), даже если вы устанавливаете для его параметра nCount значение 2.

  • вы безоговорочно вызываете ProcessMessages(), даже когда MsgWaitForMultipleObjects()не сообщает вам, что сообщения ожидают обработки.

  • Предложение until вашего цикла проверяет неправильное значение завершения, поэтому ваш цикл преждевременно прервется при ЛЮБОМ условии, которое нетайм-аут, например: когда либо завершается процесс, либо когда сообщение находится в очереди.

  • есть важное предостережение с установкой для параметра bWaitAll значения True, что вам необходимознать об этом - см. MsgWaitForMultipleObjects - очень сложный API на MSDN для получения подробной информации об этом.

С учетом вышесказанного, попробуйте что-то более похожее на это:

var
  ...
  arrHandles: array[0..1] of THandle;
  numHandles, i: Integer;
begin
  ...

  ccOk := CreateProcess(nil, PChar(ExecutableFirst + ' ' + CommandsFirst), @saSecurity, @saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess);
  if not ccOk then
    RaiseLastOSError;

  CloseHandle(piProcess.hThread);
  arrHandles[0] := piProcess.hProcess;
  numHandles := 1;

  try
    ccOk := CreateProcess(nil, PChar(ExecutableSecond + ' ' + CommandsSecond), @saSecurity, @saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess);
    if not ccOk then
      RaiseLastOSError;

    CloseHandle(piProcess.hThread);
    arrHandles[1] := piProcess.hProcess;
    numHandles := 2;

    // there is a caveat when setting bWaitAll=True that the wait will not be
    // completely satisfied until both handles are signaled AND the calling thread
    // receives an input event!  That last caveat is not desirable, so setting
    // bWaitAll=False instead to avoid that so the loop can break immediately when
    // both handles are signaled...
    repeat
      Running := MsgWaitForMultipleObjects(numHandles, arrHandles, False, INFINTE, QS_ALLINPUT);
      if {(Running >= WAIT_OBJECT_0) and} (Running < (WAIT_OBJECT_0 + DWORD(numHandles))) then
      begin
        i := Integer(Running - WAIT_OBJECT_0);
        CloseHandle(arrHandles[i]);
        if i = 0 then arrHandles[0] := arrHandles[1];
        Dec(numHandles);
      end
      else if Running = (WAIT_OBJECT_0 + DWORD(numHandles)) then begin
        Application.ProcessMessages;
      end
      else if Running = WAIT_FAILED then begin
        RaiseLastOSError;
      end;
    until numHandles = 0;
  except
    for i := 0 to numHandles-1 do begin
      TerminateProcess(arrHandles[i], 0);
      CloseHandle(arrHandles[i]);
    end;
    raise;
  end;

  BidsConversion; //run this when both processes have finished without error

  ...
end;

Как говорится,рассмотрите возможность асинхронного ожидания в отдельном рабочем потоке, чтобы больше не блокировать основной поток пользовательского интерфейса.Вы можете создать свой собственный поток, который вызывает WaitForMultipleObjects() (не MsgWaitForMultipleObjects(), поскольку вам больше не нужно ждать в очереди сообщений), или вы можете использовать RegisterWaitForSingleObject() для каждого дескриптора процесса в отдельности.В любом случае, пусть рабочий поток (-ы) уведомляет основной поток пользовательского интерфейса о завершении ожидания, и просто не вызывайте BidsConversion(), пока не получите уведомление о завершении обоих процессов.

...