Winapi 从管道读取时Win32 ReadFile挂起

Winapi 从管道读取时Win32 ReadFile挂起,winapi,freeze,readfile,Winapi,Freeze,Readfile,我正在创建一个子进程,并读取其输出。当子进程创建输出(cmd/c echo Hello World)时,我的代码工作正常,但如果进程不创建输出(cmd/c echo Hello World>output.txt),ReadFile将挂起。我只是在过程结束后才阅读 我做错什么了吗?在同步模式下,还是必须使用异步模式?所有这些都是在一个单独的线程中发生的,所以我不认为异步模式会给我带来任何好处,除非这是让它工作的唯一方法。非常感谢 saAttr.nLength = sizeof(SECURITY_A

我正在创建一个子进程,并读取其输出。当子进程创建输出(
cmd/c echo Hello World
)时,我的代码工作正常,但如果进程不创建输出(
cmd/c echo Hello World>output.txt
),ReadFile将挂起。我只是在过程结束后才阅读

我做错什么了吗?在同步模式下,还是必须使用异步模式?所有这些都是在一个单独的线程中发生的,所以我不认为异步模式会给我带来任何好处,除非这是让它工作的唯一方法。非常感谢

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
saAttr.bInheritHandle = TRUE; 
saAttr.lpSecurityDescriptor = NULL; 

CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);

memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO); 
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, commandWideString, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);

while(1)
{
    GetExitCodeProcess(piProcInfo.hProcess, &processExitCode);
    if(processExitCode != STILL_ACTIVE)
        break;
    else
        Sleep(1);
}

*output = (char *)calloc(32, sizeof(char));
processOutputSize = 0;
while(1)
{
    bSuccess = ReadFile( g_hChildStd_OUT_Rd, processOutputTemp, 32, &dwRead, NULL);
    if(!bSuccess || !dwRead)
        break;
    memcpy(*output + processOutputSize, processOutputTemp, dwRead);
    processOutputSize += dwRead;
    if(dwRead == 32)
        *output = (char *)realloc(*output, processOutputSize + 32);
    else
    {
        memset(*output + processOutputSize, 0, 1);
        break;
    }
}
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);

将进程的输出重定向到管道,启动进程,等待它退出,然后读取输出

问题是windows缓冲的数据量有限。因此,您必须在进程仍在运行时读取管道,否则进程将被阻止,因为它无法向管道写入更多数据。

您可以在这样的循环中使用:

for (;;)
{
    DWORD bytesAvail = 0;
    if (!PeekNamedPipe(stdoutPipeRead, NULL, 0, NULL, &bytesAvail, NULL)) {
        std::cout << "Failed to call PeekNamedPipe" << std::endl;
    }
    if (bytesAvail) {
        CHAR buf[BUFSIZE];
        DWORD n;
        BOOL success = ReadFile(stdoutPipeRead, buf, BUFSIZE, &n, NULL);
        if (!success || n == 0) {
            std::cout << "Failed to call ReadFile" << std::endl;
        }
        std::cout << std::string(buf, buf + n);
    }
}
(;;)的

{
DWORD-bytesAvail=0;
if(!PeekNamedPipe(stdoutPipeRead,NULL,0,NULL,&字节可用,NULL)){

std::cout您应该在读取输出管道之前关闭输出管道的写入端,正如@Marcus在评论中建议的那样

CloseHandle(g_hChildStd_OUT_Wr);


对我来说,这才是真正的答案。

我处理过一个阻塞的readfile,通常是一个未关闭的句柄导致读取阻塞。在子进程退出后,您可以将句柄关闭到管道的写入端。这有帮助吗?@Marcus它有帮助。如果进程返回输出readfile,将正常成功。如果进程退出,则不返回输出,ReadFile将失败,GetLastError()will返回代码109-错误管道破裂。这是预期行为吗?谢谢。非常感谢Wimmel。如果有人想知道,他回答了我在这里发布的问题@mattburnett抱歉,我以为这是同一个问题,只是用了不同的措辞。我读得不够好。也许是睡眠()测试之间需要延迟。我测试了它,我可以确认这是真正的解决方案。