ERROR_BROKEN_PIPE 无法读取进程终止前产生的进程输出
ERROR_BROKEN_PIPE cannot read process output produced before process termination
我在 Win32 中有一个简单的进程重定向例程。这里的问题是,如果我在从子进程 stdout 的读取之间放置一个 Sleep,一旦进程在我睡眠时终止,我就会错过输出 ERROR_BROKEN_PIPE 的管道中的最后一个字节。看起来,一旦子进程终止,它的管道和关联的句柄就会关闭,任何未决的东西都会被丢弃。唯一的解决方案似乎是尽可能快地从管道中读取文件,但由于软件的设计,这对我来说不仅仅是一个问题。
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bSuccess;
WCHAR szCmdLine[MAX_PATH];
char chBuf[BUFSIZE];
DWORD dwRead;
HANDLE g_hChildStd_OUT_Rd2 = NULL;
HANDLE g_hChildStd_OUT_Wr2 = NULL;
SECURITY_ATTRIBUTES saAttr2;
STARTUPINFO si2;
PROCESS_INFORMATION pi2;
ZeroMemory( &si2, sizeof(si2) );
si2.cb = sizeof(si2);
ZeroMemory( &pi2, sizeof(pi2) );
//create pipe
saAttr2.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr2.bInheritHandle = TRUE;
saAttr2.lpSecurityDescriptor = NULL;
assert(CreatePipe(&g_hChildStd_OUT_Rd2, &g_hChildStd_OUT_Wr2, &saAttr2, 0));
//create child process
bSuccess = FALSE;
memset(szCmdLine, 0, MAX_PATH);
wsprintf(szCmdLine, L"c:\myprocess.exe");
ZeroMemory( &pi2, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si2, sizeof(STARTUPINFO) );
si2.cb = sizeof(STARTUPINFO);
si2.hStdOutput = g_hChildStd_OUT_Wr2;
si2.hStdError = g_hChildStd_OUT_Wr2; // also add the pipe as stderr!
si2.dwFlags |= STARTF_USESTDHANDLES;
assert(CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si2, &pi2));
//read from pipe
CloseHandle(g_hChildStd_OUT_Wr2);
memset(chBuf, 0, BUFSIZE);
for (;;)
{
DWORD dwRead = 0;
DWORD bytes = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd2,NULL,NULL,NULL,&bytes,NULL)) {
//printf("Peek named pipe failed!");
break;
}
if (bytes != 0) {
if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE, &dwRead, NULL))
{
printf("EOF!!\n");
break;
}
else {
chBuf[dwRead] = 0;
printf("%s", chBuf);
}
} else {
Sleep(5000);
}
}
while(1) {
Sleep(1000);
printf("Lopp!\n");
}
return 0;
}
有什么提示吗?有没有办法让进程暂停,就像 POSIX 中发生的那样,直到它的管道被读取?
谢谢!
我觉得问题不大
你的代码确实有一些问题。许多都是未成年人:你初始化了两次 si2 和 pi2(无害),你有一个无限循环所以你的程序永远不会结束(我假设你是 Ctrl-C ...),你正在混合 _tmain
和 WCHAR(应该是wmain
和 WCHAR
或 _tmain
和 TCHAR
) 但如果你做一个 unicode 构建就没问题。
一个更严重:chBuf
的大小为 BUFSIZE
,您最多读取其中的 BUFSIZE
个字符(到这里为止还不错)。但是如果你确实读取了 BUFSIZE 个字符,dwRead
是 BUFSIZE
并且在下一行你有一个缓冲区溢出
chBuf[dwRead] = 0; // buffer overflow if BUFSIZE chars have been read !
我只用 :
解决了这个问题
if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE - 1, &dwRead, NULL))
程序运行正常。
顺便说一句:系统知道子进程(即使已终止),直到它的所有句柄都已关闭......并且你在 pi2.hProcess
.
中有一个句柄
当然不能用c:\myprocess.exe
,用cmd /c dir
测试了。因此,如果上述修复还不够,请尝试使用与我相同的子项,因为问题可能来自以 NULL hStdInput
开头的 myprocess.exe
好的,仅供参考,我想分享一下我的经验以及我是如何解决问题的。
我生成的子进程可能有问题,但据我所知,如果我以非阻塞模式调用 Read,那么只有在 PeekNamedPipe 之后,没有什么可以阻止进程被释放,即使我保留对管道的引用。
我已经解决了启动另一个阻塞管道描述符读取的线程,我不再丢失最后一个字节..
我在 Win32 中有一个简单的进程重定向例程。这里的问题是,如果我在从子进程 stdout 的读取之间放置一个 Sleep,一旦进程在我睡眠时终止,我就会错过输出 ERROR_BROKEN_PIPE 的管道中的最后一个字节。看起来,一旦子进程终止,它的管道和关联的句柄就会关闭,任何未决的东西都会被丢弃。唯一的解决方案似乎是尽可能快地从管道中读取文件,但由于软件的设计,这对我来说不仅仅是一个问题。
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bSuccess;
WCHAR szCmdLine[MAX_PATH];
char chBuf[BUFSIZE];
DWORD dwRead;
HANDLE g_hChildStd_OUT_Rd2 = NULL;
HANDLE g_hChildStd_OUT_Wr2 = NULL;
SECURITY_ATTRIBUTES saAttr2;
STARTUPINFO si2;
PROCESS_INFORMATION pi2;
ZeroMemory( &si2, sizeof(si2) );
si2.cb = sizeof(si2);
ZeroMemory( &pi2, sizeof(pi2) );
//create pipe
saAttr2.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr2.bInheritHandle = TRUE;
saAttr2.lpSecurityDescriptor = NULL;
assert(CreatePipe(&g_hChildStd_OUT_Rd2, &g_hChildStd_OUT_Wr2, &saAttr2, 0));
//create child process
bSuccess = FALSE;
memset(szCmdLine, 0, MAX_PATH);
wsprintf(szCmdLine, L"c:\myprocess.exe");
ZeroMemory( &pi2, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si2, sizeof(STARTUPINFO) );
si2.cb = sizeof(STARTUPINFO);
si2.hStdOutput = g_hChildStd_OUT_Wr2;
si2.hStdError = g_hChildStd_OUT_Wr2; // also add the pipe as stderr!
si2.dwFlags |= STARTF_USESTDHANDLES;
assert(CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si2, &pi2));
//read from pipe
CloseHandle(g_hChildStd_OUT_Wr2);
memset(chBuf, 0, BUFSIZE);
for (;;)
{
DWORD dwRead = 0;
DWORD bytes = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd2,NULL,NULL,NULL,&bytes,NULL)) {
//printf("Peek named pipe failed!");
break;
}
if (bytes != 0) {
if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE, &dwRead, NULL))
{
printf("EOF!!\n");
break;
}
else {
chBuf[dwRead] = 0;
printf("%s", chBuf);
}
} else {
Sleep(5000);
}
}
while(1) {
Sleep(1000);
printf("Lopp!\n");
}
return 0;
}
有什么提示吗?有没有办法让进程暂停,就像 POSIX 中发生的那样,直到它的管道被读取?
谢谢!
我觉得问题不大
你的代码确实有一些问题。许多都是未成年人:你初始化了两次 si2 和 pi2(无害),你有一个无限循环所以你的程序永远不会结束(我假设你是 Ctrl-C ...),你正在混合 _tmain
和 WCHAR(应该是wmain
和 WCHAR
或 _tmain
和 TCHAR
) 但如果你做一个 unicode 构建就没问题。
一个更严重:chBuf
的大小为 BUFSIZE
,您最多读取其中的 BUFSIZE
个字符(到这里为止还不错)。但是如果你确实读取了 BUFSIZE 个字符,dwRead
是 BUFSIZE
并且在下一行你有一个缓冲区溢出
chBuf[dwRead] = 0; // buffer overflow if BUFSIZE chars have been read !
我只用 :
解决了这个问题if (!ReadFile( g_hChildStd_OUT_Rd2, chBuf, BUFSIZE - 1, &dwRead, NULL))
程序运行正常。
顺便说一句:系统知道子进程(即使已终止),直到它的所有句柄都已关闭......并且你在 pi2.hProcess
.
当然不能用c:\myprocess.exe
,用cmd /c dir
测试了。因此,如果上述修复还不够,请尝试使用与我相同的子项,因为问题可能来自以 NULL hStdInput
myprocess.exe
好的,仅供参考,我想分享一下我的经验以及我是如何解决问题的。 我生成的子进程可能有问题,但据我所知,如果我以非阻塞模式调用 Read,那么只有在 PeekNamedPipe 之后,没有什么可以阻止进程被释放,即使我保留对管道的引用。 我已经解决了启动另一个阻塞管道描述符读取的线程,我不再丢失最后一个字节..