我已经编写了一个程序a.exe
,该程序使用CreateProcess函数启动我编写的另一个程序b.exe
。调用者创建两个管道,并将两个管道的写入端传递给CreateProcess,作为stdout/stderr句柄用于子进程。这几乎与MSDN上的Creating a Child Process with Redirected Input and Output示例相同。如何正确读取子进程的stdout/stderr输出?
由于似乎它不能够使用一个同步调用,它等待过程退出在任stdout或stderr可用或数据(WaitForMultipleObjects功能不上的导管的工作),则调用者有两个线程运行,它们都在stdout/stderr管道的读取端执行(阻塞)ReadFile调用;这里是它用于标准输出/标准错误的“读线程程序完全一样的代码(我没有写这个代码我自己,我认为一些同事所做的那样):
DWORD __stdcall ReadDataProc(void *handle)
{
char buf[ 1024 ];
DWORD nread;
while (ReadFile((HANDLE)handle, buf, sizeof(buf), &nread, NULL) &&
GetLastError() != ERROR_BROKEN_PIPE) {
if (nread > 0) {
fwrite(buf, nread, 1, stdout);
}
}
fflush(stdout);
return 0;
}
a.exe
然后使用一个简单的WaitForSingleObject调用等到b.exe
终止。一旦该调用返回,两个读取线程将终止(因为管道已损坏),并且使用CloseHandle关闭了两个管道的读取结束。现在
,我打的问题是这样的:b.exe
威力(根据用户输入)推出其寿命比b.exe
本身长的外部进程,守护进程基本上是这样。在这种情况下发生的情况是,stdout/stderr管道的写入结束被继承到该守护进程,因此管道永远不会被破坏。这意味着a.exe
中的WaitForSingleObject调用返回(因为b.exe
已完成),但在任一管道块上调用CloseHandle,因为两个读取线程仍处于其阻塞的ReadFile调用中。
在b.exe
返回后,如何解决这个问题,而不用蛮力终止两个读线程(TerminateThread)?如果可能的话,我想避免任何涉及管道和/或过程轮询的解决方案。
UPDATE:这是我试过到目前为止:
- 由于没有
b.exe
继承a.exe
;这不起作用。 MSDN明确指出传递给CreateProcess的句柄必须是可继承的。 - 清除stdout/stderr里面的可继承标志
b.exe
:似乎没有任何作用(如果它确实会让我感到意外)。 - 让
ReadDataProc
程序(可读取两个管道)考虑除检查ERROR_BROKEN_PIPE
之外,b.exe
是否实际运行。这当然不起作用(但我只是在后来才意识到),因为线程在ReadFile调用中被阻塞。
也许尝试CancelSynchronousIO? – adf88 2010-07-02 09:08:34
@一个df88:根据WaitForMultipleObjects上的MSDN页面,它可以适用于各种各样的东西 - 但不是管道。我的实验似乎证实了这一点:即使在没有可用数据的情况下,管道的读取结束也始终发出信号。 CancelSynchronousIO()函数看起来不错,但它不适合我,因为它只能在Windows Vista和更新版本上使用。 – 2010-07-02 09:21:16
可能的重复:http://stackoverflow.com/questions/593175/breaking-readfile-blocking-named-pipe-windows-api – adf88 2010-07-03 07:09:47