Windows:如何使用CreateProcess停止重定向Stdout的缓冲
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows:如何使用CreateProcess停止重定向Stdout的缓冲相关的知识,希望对你有一定的参考价值。
我正在使用管道从命令行可执行文件中获取重定向的stdout输出。不幸的是,在完成该过程之前,我没有得到任何输出。可执行文件在运行时输出进度状态,这就是我要解析的内容。
BOOL RunCmd( char *pCmd,
char *pWorkingDir,
int nWaitSecs,
BOOL fRegImport,
DWORD *pdwExitCode )
{
BOOL fSuccess = TRUE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sFileSecurity;
ZeroMemory( &sFileSecurity, sizeof( sFileSecurity ) );
sFileSecurity.nLength = sizeof( sFileSecurity );
sFileSecurity.bInheritHandle = TRUE;
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
fSuccess = CreatePipe( &hReadPipe, &hWritePipe, &sFileSecurity, 0 );
SetHandleInformation( hReadPipe, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &si, sizeof(si) );
ZeroMemory( &pi, sizeof(pi) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
si.wShowWindow = SW_HIDE;
int rc;
// Start the child process.
rc = CreateProcess( NULL, // No module name (use command line).
pCmd, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
TRUE,
CREATE_NO_WINDOW,
NULL, // Use parent's environment block.
pWorkingDir, // Working folder
&si, // Pointer to STARTUPINFO structure.
&pi ); // Pointer to PROCESS_INFORMATION structure.
if( ! rc )
return FALSE;
// Wait until child process exits.
DWORD dwWaitResult;
DWORD dwTimeStart = ::GetTickCount();
DWORD dwTimeNow;
#define BUFSIZE 4096
DWORD dwRead = 0;
DWORD dwAvail;
CHAR chBuf[ BUFSIZE ];
BOOL bSuccess = TRUE;
for( ;; )
{
dwTimeNow = ::GetTickCount();
dwWaitResult = ::WaitForSingleObject( pi.hProcess, ONE_SECOND );
dwRead = 0;
for( dwAvail = 0; PeekNamedPipe( hReadPipe, 0, 0, 0, &dwAvail, 0 ) && dwAvail; dwAvail = 0 )
{
dwRead = 0;
ReadFile( hReadPipe, chBuf, min( BUFSIZE, dwAvail ), &dwRead, NULL );
if( dwRead > 0 )
{
FILE *op = fopen( "c:\REDIR.OUT", "a" );
if( op )
{
fwrite( chBuf, 1, dwRead, op );
fclose( op );
}
}
}
if( dwWaitResult == WAIT_OBJECT_0 )
{
DWORD dwExitCode;
GetExitCodeProcess( pi.hProcess, &dwExitCode );
if( pdwExitCode )
(*pdwExitCode) = dwExitCode;
break;
}
if( dwWaitResult == WAIT_TIMEOUT )
{
if( dwTimeNow - dwTimeStart < (DWORD)( ONE_SECOND * nWaitSecs ) )
continue;
else
{
fSuccess = FALSE;
break;
}
}
fSuccess = FALSE;
break;
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hReadPipe );
CloseHandle( hWritePipe );
return fSuccess;
}
每秒调用PeekNamedPipe()调用,每次调用dwAvail为零,直到进程完成。
如何更快地从过程中获得输出?从控制台运行进程时,我看到进度输出。该过程将在其输出中使用“ r”以在同一行的开头显示百分比。
注意:我的回答仅涉及使用MSVC编译的可执行文件。
缓冲策略在Microsoft C运行时(CRT)库中编码。你可以了解详情here。本文建议使用控制台句柄和操作控制台缓冲区来接收无缓冲的输出。
但是,Microsoft C运行时内部有一个未记录的功能,使用lpReserved2
结构的cbReserved2
和STARTUPINFO
字段直接从其父进程继承一些内部标志的文件句柄。您可以在Microsoft Visual Studio提供的crt源代码中找到详细信息。或者在GitHub上搜索类似posfhnd
的内容。
我们可以利用这个未记录的功能来提供管道句柄并为子进程指定FOPEN | FDEV
标志,以欺骗子进程处理该管道处理与FILE_TYPE_CHAR
句柄相同的方式。
我有一个工作Python3 script来演示这种方法。
以上是关于Windows:如何使用CreateProcess停止重定向Stdout的缓冲的主要内容,如果未能解决你的问题,请参考以下文章
(C++,Windows)生成没有控制台窗口的子进程(使用 CreateProcess)