1

我使用的代码如下:

 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但第一个进程仍未完成,因此引发了异常。

为什么应用程序不等待两个进程完成然后触发程序?

4

1 回答 1

2

您没有检查 2nd 的返回值CreateProcess()是否失败,但更重要的是您完全滥用了MsgWaitForMultipleObjects()

  • 即使您将其参数设置为 2,您也没有将两个进程句柄传递给。MsgWaitForMultipleObjects()nCount

  • 你是ProcessMessages()无条件地打电话,即使MsgWaitForMultipleObjects()没有告诉你消息正在等待处理。

  • 您的循环的until子句正在检查错误的终止值,因此您的循环将在任何不是超时的条件下过早中断,例如:当任一进程完成时,或者当队列中的消息未决时。

  • 将参数设置为 True 有一个重要的警告bWaitAll,您需要注意 - 有关详细信息,请参阅MsgWaitForMultipleObjects 是 MSDN 上的一个非常棘手的 API

话虽如此,尝试更像这样的东西:

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;

话虽如此,请考虑在单独的工作线程中异步执行等待,这样您就不会再阻塞主 UI 线程了。您可以创建自己的调用线程WaitForMultipleObjects()MsgWaitForMultipleObjects()因为您不再需要在消息队列上等待),或者您可以RegisterWaitForSingleObject()单独在每个进程句柄上使用。BidsConversion()无论哪种方式,让工作线程在等待完成时通知主 UI 线程,并且在您收到两个进程都已完成的通知之前不要调用。

于 2019-05-20T18:53:53.267 回答