1

I run a command to cmd.exe with CreateProcess(). The command itself has an endless output, so I use a function that I modified from this answer to get partial outputs into a string.

#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;

std::string ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
    DWORD dwRead; 
    CHAR chBuf[BUFSIZE];
    ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
    std::string s(chBuf, dwRead);
    return s;
}

But this code creates some problems.

Firstly, every time I call it, it may freeze the program while it waits for the output to buffer to 4096 bytes.

Secondly, it will always only get the next 4096 bytes in the output queue. (even if the current output is a lot larger)

What I would like would be to call the function and get all the data that was outputed in the meanwhile and also be able to set a minimum number of bytes to get (instead of the buffer). If the minimum number of bytes is not available yet, I would prefer it skips ReadFile() entirely and just return false. (instead of freezing the application)

Would this be possible?

Community
  • 1
  • 1
Jaruba
  • 1,055
  • 11
  • 22
  • 2
    try this: http://stackoverflow.com/questions/593175/breaking-readfile-blocking-named-pipe-windows-api –  Jan 28 '15 at 17:05
  • It shouldn't wait for all 4096 bytes. It should return as soon as there is at least one byte available. Are you sure this problem really exists? NB you *must* check for an error return. – user207421 Jan 29 '15 at 03:10
  • It might be because I'm building a FireBreath project and I'm calling the C++ function from the browser, so it could also freeze because the browser can't continue until it gets the return value. But I am sure that the function waits until it gets the entire buffer and only then returns the value. My answer solves this issue entirely. – Jaruba Jan 29 '15 at 03:42

2 Answers2

2

Arkadiy's comment led me in the right direction. I made it work with the help of PeekNamedPipe()

My current code:

#include <string>
#include <iostream>
#include <windows.h> 
#include <stdio.h>

#pragma warning( disable : 4800 ) // hide bool warning
#define BUFSIZE 1024
HANDLE g_hChildStd_OUT_Rd = NULL;

std::string ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
    CHAR chBuf[BUFSIZE];
    DWORD dwRead;
    DWORD avail;
    bool bSuccess = FALSE;
    bool tSuccess = FALSE;
    std::string out = "";
    tSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL, &avail, NULL );
    if (tSuccess && avail >= BUFSIZE) {
        while (avail >= BUFSIZE) {
            bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
            if( ! bSuccess || dwRead == 0 ) break; 
            std::string s(chBuf, dwRead);
            out += s;
            avail = avail - BUFSIZE;
        }
        return out;
    } else {
        return "[false]";
    }
}

This function, when called, will return the output since the last time it was called. BUFSIZE now acts as the minimum buffer, if the output is less then the minimum buffer, the function will return [false] (as string, not bool).

Jaruba
  • 1,055
  • 11
  • 22
0

One option is to put the code that reads from the pipe in its own thread, and have it run in a loop. That will keep your main thread from blocking, and you can always read as much data as there is. The challenge is that you now need to have a way to get the reading thread to transmit the data back to the main thread.

Another option is to use overlapped I/O, which is discussed in the ReadFile and CreateFile documentation in MSDN. It's a way to have the OS call back whenever data has been read from the pipe. So your ReadFile won't block, but there (probably) won't be any data in the buffer until later.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175