来自子进程的 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,但这可能是相关的。
我正在尝试创建一个子进程并使用 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,但这可能是相关的。