【问题标题】:Win32, ReadFile from pipe block even after child terminatedWin32,即使在孩子终止后也从管道块中读取文件
【发布时间】:2026-01-07 17:10:01
【问题描述】:

我有一个简单的程序(用 C 语言),它创建两个子进程,每个子进程等待一个继承的管道,然后将输出放在一个文件中。

一切正常,除了在两个管道上的一些写/读循环之后,当孩子结束时,对 ReadFile 的调用阻塞,等待管道上的数据。我使用以下模式:

...
//create pipe1
CreatePipe(&hReadDup,&hWrite,&saAttr,0);
DuplicateHandle(GetCurrentProcess(),hReadDup,GetCurrentProcess(),&hRead,0,FALSE,DUPLICATE_SAME_ACCESS);
CloseHandle(hReadDup);


si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = hWrite;   

CreateProcess(  NULL,
        const_cast<LPWSTR>(cmd2.c_str()), //the command to execute
        NULL,
        NULL,
        TRUE,
        0,
        NULL,
        NULL,
        &si, //si.
        &pi
    );

...
CloseHandle(hWrite); // EDIT: this was the operation not properly done!

while(cont){
    ...
    cont = ReadFile(hRead,buf,50, &actual,NULL);
    ...
}
... 

最后一次调用(子进程退出后)阻塞。 知道为什么(如果没有,如何调试)?

【问题讨论】:

  • 感谢您的提问和回答。我一直在 _popen(...) 变体(在我的 Code::Blocks-GCC 环境中运行良好,但在我的工作环境中根本不运行)和您使用的 CreateProcess/CreatePipe 方法之间来回切换。您的实施效果很好。谢谢。
  • 谢谢,今天遇到了这个!

标签: c pipe win32-process


【解决方案1】:

我自己找到了解决方案(实际上是一个编码错误)。 我没有正确关闭管道的父级写入句柄 (hWrite),因此,同步 ReadFile 无法向我报告子进程终止。

如果有人遇到同样的问题,请确保在开始对该管道的 I/O 操作之前关闭该管道的可继承句柄(正如 MSDN 报告的那样,无法再次找到)。

【讨论】:

【解决方案2】:

您正在同步模式下调用ReadFile()。只要管道打开,ReadFile() 就会阻塞等待更多数据。如果您将CreateProcess() 返回给您的进程和线程句柄保持打开状态,这将阻止子进程完全退出,因此管道可能不会在子端关闭。在进入你的阅读循环之前,关闭CreateProcess()返回的句柄,让管道在子进程完全终止时正确关闭,然后ReadFile()可以在它不能再从管道读取时向你报告错误.或者,切换到管道上的重叠 I/O,这样您就可以在循环运行时使用 WaitForSingleObject()GetExitCodeProcess() 监视子进程,这样无论管道状态如何,您都可以检测到子进程何时终止。

【讨论】:

  • 实际上,在循环终止之前,我不能等待子进程终止。此外,我自己解决了这个问题:我没有正确关闭管道的父级写句柄,因此同步ReadFile 无法向我报告子进程终止。还是谢谢你
  • @Remy,你确定吗?我从来没有听说过持有一个进程的打开句柄会阻止 Windows 在它退出时关闭属于该进程的句柄。
  • 我有同样的问题和同样的解决方案 - 我怀疑这是因为,如果你不关闭句柄,父进程可能会写入管道,然后从同一个管道本身读取。因此,ReadFile 必须等待/管道必须保持打开状态,因为上述(愚蠢的)情况仍然可能发生。但是,如果不再存在句柄,那么就再也无法写入管道了。
【解决方案3】:

在您的情况下,一切都很好,您可以访问管道上的两个进程。但是,如果您没有,或者只是想中断ReadFile 通话,那么CancelSynchronousIo 就是您的朋友:https://msdn.microsoft.com/en-us/library/windows/desktop/aa363789(v=vs.85).aspx

【讨论】: