我知道之前曾对此问题提出过许多类似的问题,但到目前为止,我还没有找到真正可行的解决方案。我想从我的程序启动一个控制台程序并捕获其输出。我的实现应采用与WaitForMultipleObjects()
兼容的方式,即,我希望在管道中要读取新数据时得到通知。
我的实现基于MSDN上的this example。但是,我不得不对其进行一些修改,因为我需要重叠的I / O,以便能够等待ReadFile()
完成。因此,我使用的是使用here中Dave Hart的MyCreatePipeEx()
函数创建的命名管道。
这是我的实际代码。我出于可读性原因删除了错误检查。
HANDLE hReadEvent;
HANDLE hStdIn_Rd, hStdIn_Wr;
HANDLE hStdOut_Rd, hStdOut_Wr;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
OVERLAPPED ovl;
HANDLE hEvt[2];
DWORD mask, gotbytes;
BYTE buf[4097];
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
MyCreatePipeEx(&hStdOut_Rd, &hStdOut_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
MyCreatePipeEx(&hStdIn_Rd, &hStdIn_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
SetHandleInformation(hStdOut_Rd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hStdIn_Wr, HANDLE_FLAG_INHERIT, 0);
memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hStdOut_Wr;
siStartInfo.hStdOutput = hStdOut_Wr;
siStartInfo.hStdInput = hStdIn_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "test.exe", NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
int i = 0;
hEvt[i++] = piProcInfo.hProcess;
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = hReadEvent;
if(!ReadFile(hStdOut_Rd, buf, 4096, &gotbytes, &ovl)) {
if(GetLastError() == ERROR_IO_PENDING) hEvt[i++] = hReadEvent;
} else {
buf[gotbytes] = 0;
printf("%s", buf);
}
mask = WaitForMultipleObjects(i, hEvt, FALSE, INFINITE);
if(mask == WAIT_OBJECT_0 + 1) {
if(GetOverlappedResult(hStdOut_Rd, &ovl, &gotbytes, FALSE)) {
buf[gotbytes] = 0;
printf("%s", buf);
}
} else if(mask == WAIT_OBJECT_0) {
break;
}
}
此代码的问题如下:如您所见,我正在使用ReadFile()
读取4kb的块,因为我显然不知道外部程序test.exe
将要处理多少数据输出。建议这样做here:
要从客户端进程中读取可变数量的数据,只是发出 阅读您认为方便的任何大小的请求,并做好准备 处理比您要求的短的读取事件。别 将较短但非零的长度解释为EOF。继续阅读 请求,直到您读取零长度或错误。
但是,这不起作用。作为ReadFile()
结构一部分传递给OVERLAPPED
的事件对象仅在缓冲区中有4kb时才触发。如果外部程序仅打印“ Hello”,则该事件根本不会触发。 hReadEvent
的缓冲区中必须有4kb才能真正触发。
所以我认为我应该逐字节读取,并修改程序以使用ReadFile()
,如下所示:
if(!ReadFile(hStdOut_Rd, buf, 1, &gotbytes, &ovl)) {
但是,这也不起作用。如果我这样做,根本不会触发读取事件,这真的使我感到困惑。如果使用4096字节,则事件确实会在管道中有4096字节时立即触发,但是使用1字节时,该事件将完全无效。
那我应该怎么解决呢?我在这里几乎没有想法。每当管道中有一些新数据时,是否没有办法触发ReadFile()
事件?不会那么困难,是吗?