无需等待缓冲区的 Win32 ReadFile 输出
Win32 ReadFile Output without waiting for buffer
我 运行 使用 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()
而只是 return false
。 (而不是冻结应用程序)
这可能吗?
一个选择是将从管道读取的代码放在它自己的线程中,并使其 运行 进入循环。这将使您的主线程不会阻塞,并且您始终可以读取尽可能多的数据。挑战在于您现在需要有一种方法让读取线程将数据传回主线程。
另一种选择是使用重叠 I/O,这在 MSDN 的 ReadFile 和 CreateFile 文档中进行了讨论。这是一种在从管道读取数据时让 OS 回调的方法。所以你的 ReadFile 不会阻塞,但是(可能)直到稍后缓冲区中才会有任何数据。
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]";
}
}
调用此函数时,将 return 自上次调用后的输出。 BUFSIZE
现在充当最小缓冲区,如果输出小于最小缓冲区,函数将 return [false]
(作为字符串,而不是布尔值)。
我 运行 使用 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()
而只是 return false
。 (而不是冻结应用程序)
这可能吗?
一个选择是将从管道读取的代码放在它自己的线程中,并使其 运行 进入循环。这将使您的主线程不会阻塞,并且您始终可以读取尽可能多的数据。挑战在于您现在需要有一种方法让读取线程将数据传回主线程。
另一种选择是使用重叠 I/O,这在 MSDN 的 ReadFile 和 CreateFile 文档中进行了讨论。这是一种在从管道读取数据时让 OS 回调的方法。所以你的 ReadFile 不会阻塞,但是(可能)直到稍后缓冲区中才会有任何数据。
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]";
}
}
调用此函数时,将 return 自上次调用后的输出。 BUFSIZE
现在充当最小缓冲区,如果输出小于最小缓冲区,函数将 return [false]
(作为字符串,而不是布尔值)。