这个过程不会等吗?

时间:2019-05-20 17:24:40

标签: delphi cmd createprocess delphi-10.3-rio

我使用的代码如下:

fortify()

该代码正常工作,但有时会触发 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; ,但是“第一个流程”仍未完成,因此引发了异常。

为什么应用程序不等待两个进程都完成,然后启动该过程?

1 个答案:

答案 0 :(得分:2)

您不检查第二个CreateProcess()的返回值是否失败,但更重要的是,您完全滥用MsgWaitForMultipleObjects()

  • 即使您将其MsgWaitForMultipleObjects()参数设置为2,也不会将两者进程句柄传递给nCount

  • 您正在无条件呼叫ProcessMessages(),即使MsgWaitForMultipleObjects()并未告诉您消息正在等待处理。

  • 您的循环的until子句正在检查错误的终止值,因此,在任何非超时的条件下,您的循环都会过早中断,例如:进程完成或消息挂起时在队列中。

  • bWaitAll参数设置为True需要引起注意,这是一个重要的警告-有关此细节,请参见MSDN上的MsgWaitForMultipleObjects is a very tricky 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()。无论哪种方式,都让工作线程在等待完成时通知主UI线程,直到收到两个进程都已完成的通知,才调用BidsConversion()