Win32 ReadFile输出无需等待缓冲区

时间:2015-01-28 16:53:28

标签: c++ visual-studio-2012 readfile

我使用CreateProcess()向cmd.exe运行命令。该命令本身具有无穷无尽的输出,因此我使用我从this answer修改的函数将部分输出转换为字符串。

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

但是这段代码会产生一些问题。

首先,每当我调用它时,它可能会在程序等待输出缓冲到4096字节时冻结程序。

其次,它总是只获得输出队列中的下一个4096字节。 (即使当前的输出要大很多)

我想要的是调用函数并获取同时输出的所有数据,并且还能够设置要获取的最小字节数(而不是缓冲区)。如果还没有最小字节数,我希望它完全跳过ReadFile()并返回false。 (而不是冻结申请)

这可能吗?

2 个答案:

答案 0 :(得分:1)

Arkadiy的评论让我朝着正确的方向前进。我在PeekNamedPipe()

的帮助下完成了它的工作

我目前的代码:

#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]";
    }
}

此函数在被调用时将返回自上次调用以来的输出。 BUFSIZE现在充当最小缓冲区,如果输出小于最小缓冲区,则函数将返回[false](作为字符串,而不是bool)。

答案 1 :(得分:0)

一种选择是将从管道读取的代码放在自己的线程中,并让它在循环中运行。这样可以防止主线程被阻塞,并且您可以随时读取尽可能多的数据。挑战在于你现在需要有一种方法来让读取线程将数据传回主线程。

另一种选择是使用重叠I / O,这在MSDN的ReadFile和CreateFile文档中有所讨论。这是一种在从管道读取数据时让OS回调的方法。所以你的ReadFile不会阻塞,但是(可能)在缓冲区之前不会有任何数据。