我使用的代码如下:
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;
,但是“第一个流程”仍未完成,因此引发了异常。
为什么应用程序不等待两个进程都完成,然后启动该过程?
答案 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()
。