【发布时间】:2011-03-11 00:06:41
【问题描述】:
我编写了一个程序a.exe,它使用CreateProcess 函数启动了我编写的另一个程序b.exe。调用者创建两个管道并将两个管道的写入端作为标准输出/标准错误句柄传递给 CreateProcess,以供子进程使用。这与 MSDN 上的 Creating a Child Process with Redirected Input and Output 示例几乎相同。
因为它似乎无法使用一个同步调用来等待进程退出 或 stdout 或 stderr 上的数据可用(WaitForMultipleObjects 函数不在管道上工作),调用者有两个正在运行的线程,它们都在 stdout/stderr 管道的读取端执行(阻塞)ReadFile 调用;这是用于 stdout/stderr 的“读取线程过程”的确切代码(我自己没有编写此代码,我假设某些同事做了):
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) 终止两个读取线程的情况下解决此问题?如果可能的话,我也想避免任何涉及轮询管道和/或进程的解决方案。
更新:这是我目前尝试过的:
- 没有
b.exe继承a.exe;这不起作用。 MSDN 特别指出传递给 CreateProcess 的句柄必须是可继承的。 - 清除
b.exe内部 stdout/stderr 上的可继承标志:似乎没有任何效果(如果有的话,我会感到惊讶)。 - 让
ReadDataProc过程(在两个管道上读取)除了检查ERROR_BROKEN_PIPE之外,还要考虑b.exe是否实际运行。这当然不起作用(但我后来才意识到)因为线程在 ReadFile 调用中被阻塞了。
【问题讨论】:
-
也许试试 CancelSynchronousIO ?
-
@adf88:根据
WaitForMultipleObjects上的 MSDN 页面,它可以处理各种事情——但不是管道。我的实验似乎证实了这一点:管道的读取端总是发出信号,即使没有可用数据。 CancelSynchronousIO() 函数看起来不错,但它不适合我,因为它仅适用于 Windows Vista 和更新版本。 -
@adf88:你引用的问题看起来很相似,但它不是这个问题的重复。我面临同样的症状,但我在比其他问题更广泛的解决方案空间中寻求解决方案。
-
b.exe将FALSE传递给 CreateProcess() 的bInheritHandles参数就足够了。
标签: c++ windows winapi process