来自子进程的 ReadFile 不断返回相同的数据

ReadFile from a subprocess keeps returning same data

我正在尝试创建一个子进程并使用 windows 管道与它异步通信,然后在子进程生成输出时将其输出重定向到主进程的 cout。 这是我的代码:

HANDLE hWriteOUT, hReadOUT, hWriteIN, hReadIN;
SECURITY_ATTRIBUTES saPipe = { 0 };
PROCESS_INFORMATION procInfo = { 0 };
STARTUPINFO procSi;
DWORD dwWritten, dwRead;
int bufsize = 6000;
char buf[6000];
saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
saPipe.bInheritHandle = TRUE;
saPipe.lpSecurityDescriptor = NULL;
CreatePipe(&hReadOUT, &hWriteOUT, &saPipe, 0);
SetHandleInformation(hReadOUT, HANDLE_FLAG_INHERIT, 0);
CreatePipe(&hReadIN, &hWriteIN, &saPipe, 0);
SetHandleInformation(hWriteIN, HANDLE_FLAG_INHERIT, 0);

ZeroMemory(&procSi, sizeof(STARTUPINFO));
procSi.cb = sizeof(STARTUPINFO);
procSi.hStdError = hWriteOUT;
procSi.hStdOutput = hWriteOUT;
procSi.hStdInput = hReadIN;
procSi.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(
        NULL,
        (LPSTR) "app.exe",
        NULL, NULL, TRUE, 0, NULL, NULL, &procSi, &procInfo);

WriteFile(hWriteIN, "go movetime 1000\n", strlen("go movetime 1000\n"),
        &dwWritten, NULL);

auto start = clock();
auto seconds = 1.5;;
do {
    Sleep(500);
    cout << "cycle..." << endl;
    buf[0]=char(NULL);
    ReadFile(hReadOUT, buf, bufsize, &dwRead, NULL);
    cout << buf;
    //loop for 1.5 seconds = 3 cycles
} while ((double(clock() - start) / ((double) CLOCKS_PER_SEC)) < seconds);

问题是连续读取似乎没有按顺序访问信息。如果我简化输出,那么而不是像:

循环... 1个 2个 3个 循环... 4个 5个 6个 循环... 7 8个 完成!

我看到类似的东西:

循环... 1个 2个 3个 循环... 4个 2个 3个 循环... 8个 完毕! 4

输出的某些部分丢失了,有些被跳过了! 如果我摆脱 for 循环并在更长的延迟后只读一次,我确实得到了预期的结果。

我假设我的子进程不断将内容转储到管道中这一事实使 ReadFile 应该保留的偏移量无效。 解决方法是什么?

ReadFile 读取字节直到您传入的 bufsize 参数。它不会在末尾放置零字节,因此您不能只将缓冲区视为空终止字符串。

它 returns 第 4 个参数读取的字节数(dwRead 在你的例子中)。但是,您没有使用该函数设置到该变量中的值。

这意味着当您在循环中第二次打印缓冲区时,如果 ReadFile 调用仅读取一个字节,cout 语句仍将打印缓冲区中的值来自上一次调用的缓冲区。这就是为什么您得到“4 2 3”的原因,因为读取文件刚刚用 4 替换了 1。

解决此问题的一种方法是在缓冲区末尾留下一些 space 用于 NULL 字节,然后将 NULL 字节设置到缓冲区中,以便 cout 知道何时停止. (注意:将第一个字节设置为 NULL 不起作用,因为这是 ReadFile 将写入的第一件事)。

类似于:

ReadFile(hReadOUT, buf, bufsize - 1, &dwRead, NULL);
buf[dwRead] = '[=10=]';
cout << buf;

注意 bufsize 之后的 - 1,否则你可能会倒霉并读取 6000 字节并尝试将数组的 6001 字节设置为 [=17=],这将是缓冲区溢出。

您还可以进行一些完整性检查,以确保 dwRead 为负且小于 bufsize。

还有其他方法可以解决此问题(将您检索到的确切字节数附加到字符串中,然后在最后打印出来。或者您可以在 ReadFile 调用之前将缓冲区完全清零,但这太过分了)。

我不确定您为什么会因为这个问题而错过 5、6 和 7,但这可能是相关的。