ERROR_BROKEN_PIPE无法读取进程终止前生成的进程输出

时间:2015-01-27 12:19:28

标签: c++ winapi

我在Win32中只是一个简单的流程重定向例程。这里的问题是,如果我在子进程stdout的读取之间设置Sleep,一旦进程在我睡眠时终止,我就会错过输出ERROR_BROKEN_PIPE的管道中的最后一个字节。看来,只要子进程终止,它的管道和相关句柄就会被关闭,任何待处理的东西都会被丢弃。唯一的解决方案似乎是尽快从管道中读取ReadFile,但由于软件的设计,这对我来说不仅仅是一个问题。

int _tmain(int argc, _TCHAR* argv[])
{
    BOOL bSuccess;
    WCHAR szCmdLine[MAX_PATH];
    char chBuf[BUFSIZE];
    DWORD dwRead;
    HANDLE g_hChildStd_OUT_Rd2 = NULL;
    HANDLE g_hChildStd_OUT_Wr2 = NULL;
    SECURITY_ATTRIBUTES saAttr2; 
    STARTUPINFO si2;
    PROCESS_INFORMATION pi2;

    ZeroMemory( &si2, sizeof(si2) );
    si2.cb = sizeof(si2);
    ZeroMemory( &pi2, sizeof(pi2) );
    //create pipe
    saAttr2.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr2.bInheritHandle = TRUE; 
    saAttr2.lpSecurityDescriptor = NULL; 
    assert(CreatePipe(&g_hChildStd_OUT_Rd2, &g_hChildStd_OUT_Wr2, &saAttr2, 0));
    //create child process
    bSuccess = FALSE;
    memset(szCmdLine, 0, MAX_PATH);
    wsprintf(szCmdLine, L"c:\\myprocess.exe");
    ZeroMemory( &pi2, sizeof(PROCESS_INFORMATION) );
    ZeroMemory( &si2, sizeof(STARTUPINFO) );
    si2.cb = sizeof(STARTUPINFO); 
    si2.hStdOutput = g_hChildStd_OUT_Wr2;
    si2.hStdError = g_hChildStd_OUT_Wr2; // also add the pipe as stderr!
    si2.dwFlags |= STARTF_USESTDHANDLES;
    assert(CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si2, &pi2));
    //read from pipe
    CloseHandle(g_hChildStd_OUT_Wr2);
    memset(chBuf, 0, BUFSIZE);
    for (;;) 
    { 
        DWORD dwRead = 0;
        DWORD bytes = 0;
        if (!PeekNamedPipe(g_hChildStd_OUT_Rd2,NULL,NULL,NULL,&bytes,NULL)) {
            //printf("Peek named pipe failed!");
            break;
        }

        if (bytes != 0) {
            if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE, &dwRead, NULL)) 
            {
                printf("EOF!!\n");
                break;

            }
            else {
                chBuf[dwRead] = 0;
                printf("%s", chBuf);
            }

        } else {
            Sleep(5000);
        }

    }
    while(1) {
    Sleep(1000);
    printf("Lopp!\n");
    }
    return 0;
}

任何提示?有没有办法让进程保持不变,就像它在POSIX中发生一样,直到读取它的管道?

谢谢!

2 个答案:

答案 0 :(得分:0)

我认为问题不存在。

您的代码确实存在一些问题。很多都是未成年人:你初始化两次si2和pi2(无害),你有一个无限循环所以你的编程永远不会结束(我假设你是Ctrl-C ...),你混合_tmain和WCHAR(应该是wmainWCHAR_tmainTCHAR),但如果您执行unicode构建,则可以。

其中一个更严重:chBuf的大小为BUFSIZE,您在其中读到BUFSIZE个字符(直到此处为止)。但如果你确实读过BUFSIZE字符,那么dwReadBUFSIZE,而下一行就有缓冲区溢出

chBuf[dwRead] = 0; // buffer overflow if BUFSIZE chars have been read !

我只用以下方法解决了这个问题:

if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE - 1, &dwRead, NULL))

并且程序正常运行。

BTW:系统知道子进程(即使已终止),直到它的所有句柄都已关闭...并且您在pi2.hProcess中有句柄。

当然,我无法使用c:\\myprocess.exe并使用cmd /c dir进行测试。因此,如果上述修复程序不够,请尝试使用与我相同的子项,因为问题可能来自myprocess.exe,它以NULL hStdInput

开头

答案 1 :(得分:0)

仅供参考,我想分享一下我的经验,以及我是如何解决这个问题的。 我可能有可能在我生成的子进程中出现问题,但从我看到的情况来看,如果我在非阻塞模式下调用Read,那么只有在PeekNamedPipe之后,没有任何东西阻止进程被释放,即使我保持对管道的引用。 我已经解决了启动另一个阻塞读取管道描述符的线程,并且我将失去最后一个字节...