Matt
Matt

Reputation: 774

Win32 ReadFile hangs when reading from pipe

I am creating a child process, and reading its output. My code works fine when the child process creates output (cmd /c echo Hello World), however ReadFile will hang if process does not create output (cmd /c echo Hello World > output.txt). I am only reading after the process has terminated.

Am I doing something horribly wrong? Is there anyway to do this with synchronous mode, or do I have to use asynchronous mode? All of this is happening in a seperate thread, so I dont think asynchronous mode would offer any benefit to me, unless it is the only way to get this to work. Thanks a lot!

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

Upvotes: 9

Views: 18915

Answers (3)

Michael Konečný
Michael Konečný

Reputation: 1764

You should close the write end of the output pipe before you read from it, as @Marcus suggested in the comment.

CloseHandle(g_hChildStd_OUT_Wr);

For me this is the real answer.

Upvotes: 21

amanjiang
amanjiang

Reputation: 1283

You could use PeekNamedPipe in a loop like this:

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

Upvotes: 4

wimh
wimh

Reputation: 15232

You redirect the output of the process to a pipe, start the process, wait till it exits, and then read the output.

The problem is that windows buffers just a limited amount of data. So you must read the pipe while the process is still running, otherwise the process will be blocked because it cannot write any more data to the pipe.

Upvotes: 5

Related Questions