如何正确读取子进程的stdout / stderr输出?

时间:2010-07-02 08:36:04

标签: c++ windows winapi process

我编写了一个程序a.exe,它使用CreateProcess函数启动了我编写的另一个程序b.exe。调用者创建两个管道,并将两个管道的写入端传递给CreateProcess,作为stdout / stderr句柄用于子进程。这几乎与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)终止读取线程时如何解决这个问题?如果可能的话,我也想避免任何涉及管道和/或过程轮询的解决方案。

更新:这是我到目前为止所尝试的内容:

  1. 没有b.exe继承a.exe;这不起作用。 MSDN特别声明传递给CreateProcess的句柄必须是可继承的。
  2. 清除b.exe内的stdout / stderr上的可继承标志:似乎没有任何效果(如果它确实会让我感到惊讶)。
  3. 使用ReadDataProc过程(在两个管道上读取)除了检查b.exe之外,还要考虑ERROR_BROKEN_PIPE是否实际运行。这当然不起作用(但之后我才意识到),因为线程在ReadFile调用中被阻止。

4 个答案:

答案 0 :(得分:2)

  1. 使用命名管道和异步ReadFile

  2. 解析从管道读取的输出以查找结尾(在您的情况下可能过于复杂)。

答案 1 :(得分:1)

  

在这种情况下会发生什么   写stdout / stderr的结尾   管道继承到该守护进程   过程,所以管道永远不会破裂。

守护进程应该关闭其继承的文件描述符。

答案 2 :(得分:0)

似乎在Windows Vista之前的Windows版本上(您可以使用CancelSynchronousIO函数),无法使用TerminateThread终止读取线程。

一个合适的替代方法(suggested by adf88)可能是使用异步ReadFile调用,但在我的情况下这是不可能的(对现有代码的更改要求太多)。

答案 3 :(得分:0)

设置一些全局标志(bool exit_flag)并在a.exe中写入管道