简体   繁体   English

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

[英]ERROR_BROKEN_PIPE cannot read process output produced before process termination

I've a simply process redirection routine in Win32. 我在Win32中有一个简单的过程重定向例程。 The problem here is that, if I put a Sleep between reads from the child process stdout, as soon as the process terminates while I sleep, I simply miss the last bytes from the pipe that outputs a ERROR_BROKEN_PIPE. 这里的问题是,如果我在子进程stdout的读取之间放置一个Sleep,则当我在睡眠时终止该进程时,我只会错过输出ERROR_BROKEN_PIPE的管道中的最后一个字节。 It seems that , as soon as the child process terminates, it's pipes and associated handles are closed and anything pending discarded. 看来,一旦子进程终止,它的管道和关联的句柄就会关闭,所有待处理的东西都会被丢弃。 The only solution seems to ReadFile from the pipe as fast as possible, but this is more than a problem for me due to the design of the software. 唯一的解决方案似乎是尽可能快地从管道中读取File,但是由于软件的设计,这对我来说不只是一个问题。

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;
}

Any hint ? 有什么提示吗? Is there a way to keep the process on hold, like it happens in POSIX, until its pipes are read ? 有没有一种方法可以像在POSIX中一样保持进程直到读取其管道?

Thanks! 谢谢!

I think the problem is not there. 我认为问题不存在。

You do have some problems in your code. 您的代码中确实有一些问题。 Many are minors : you initialize twice si2 and pi2 (harmless), you have an endless loop so your prog will never end (I assume you Ctrl-C ...), you are mixing _tmain and WCHAR (should be either wmain and WCHAR or _tmain and TCHAR ) but if you do an unicode build it is fine. 许多是未成年人:您对si2和pi2进行了两次初始化(无害),您有一个无限循环,因此您的编将永无止境(我假设您是Ctrl-C ...),您正在混合_tmain和WCHAR(应为wmainWCHAR_tmainTCHAR ),但如果您执行unicode构建,则可以。

One is more serious : chBuf has size BUFSIZE , you read upto BUFSIZE chars in it (fine till here). 一种更为严重: chBuf大小为BUFSIZE ,您chBuf可以读取BUFSIZE字符(直到此处为止)。 But if you do read BUFSIZE chars, dwRead is BUFSIZE and on next line you have a buffer overflow 但是,如果您确实读取了BUFSIZE个字符,则dwReadBUFSIZE并且在下一行您将有缓冲区溢出

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

I only fixed that with : 我只用:

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

and the program works correctly. 并且程序正常运行。

BTW : the child process is knows by the system (even if terminated) until all handles to it have been closed ... and you have a handle to it in pi2.hProcess . 顺便说一句:在关闭子进程的所有句柄之前,系统都会知道子进程(即使已终止)……并且您在pi2.hProcess也拥有该子进程的句柄。

Of course I could not use c:\\\\myprocess.exe and tested with cmd /c dir . 当然,我不能使用c:\\\\myprocess.exe并使用cmd /c dir进行测试。 So if the above fix is not enough try to use same child as I did because the problem could come from myprocess.exe that is started with a NULL hStdInput 因此,如果上面的修复是不够的尝试使用同一个孩子像我一样,因为这个问题可能来自myprocess.exe即开始用NULL hStdInput

Ok for reference only, I would like to share my experience and how I solved the problem. 好的,仅供参考,我想分享我的经验以及如何解决该问题。 IT may be possible that there's something wrong in the child process I spawn, but from what I see, if I call Read in non-blocking mode, so only after a PeekNamedPipe, there's nothing that prevent the process from being deallocated, even I keep a reference to the pipe. 可能是我生成的子进程中有问题,但是从我的观察中,如果我以非阻塞模式调用Read,那么只有在PeekNamedPipe之后,没有什么可以阻止该进程被释放,即使我保持对管道的引用。 I've solved launching another thread that does blocking Read on the pipe descriptor, and I'm longer loosing the last bytes.. 我已经解决了启动另一个线程的问题,该线程确实阻止了管道描述符上的Read,并且我失去了最后一个字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM