C 如何读取子进程';谁的产量?
我编写了一个函数,它试图通过管道读取子进程的命令行输出。这应该是的一个简单子集,但我显然犯了某种错误 下面的ReadFile(…)调用将永远阻塞,无论我将其放置在WaitForSingleObject(…)调用之前还是之后,该调用都将发出子进程结束的信号 我已经阅读了所有建议“使用异步读取文件”的答案,如果有人能告诉我如何在管道上实现这一点,我愿意接受这一建议。虽然我不明白为什么在这种情况下需要异步I/OC 如何读取子进程';谁的产量?,c,windows,winapi,pipe,C,Windows,Winapi,Pipe,我编写了一个函数,它试图通过管道读取子进程的命令行输出。这应该是的一个简单子集,但我显然犯了某种错误 下面的ReadFile(…)调用将永远阻塞,无论我将其放置在WaitForSingleObject(…)调用之前还是之后,该调用都将发出子进程结束的信号 我已经阅读了所有建议“使用异步读取文件”的答案,如果有人能告诉我如何在管道上实现这一点,我愿意接受这一建议。虽然我不明白为什么在这种情况下需要异步I/O #include "stdafx.h" #include <string> #
#include "stdafx.h"
#include <string>
#include <windows.h>
unsigned int launch( const std::string & cmdline );
int _tmain(int argc, _TCHAR* argv[])
{
launch( std::string("C:/windows/system32/help.exe") );
return 0;
}
void print_error( unsigned int err )
{
char* msg = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msg,
0, NULL );
std::cout << "------ Begin Error Msg ------" << std::endl;
std::cout << msg << std::endl;
std::cout << "------ End Error Msg ------" << std::endl;
LocalFree( msg );
}
unsigned int launch( const std::string & cmdline )
{
TCHAR cl[_MAX_PATH*sizeof(TCHAR)];
memset( cl, 0, sizeof(cl) );
cmdline.copy( cl, (_MAX_PATH*sizeof(TCHAR)) - 1);
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
SECURITY_ATTRIBUTES saAttr;
memset( &saAttr, 0, sizeof(saAttr) );
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000) )
throw std::runtime_error( "StdoutRd CreatePipe" );
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0) )
throw std::runtime_error( "Stdout SetHandleInformation" );
STARTUPINFO startupInfo;
memset( &startupInfo, 0, sizeof(startupInfo) );
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
char* rawEnvVars = GetEnvironmentStringsA();
//__asm _emit 0xcc;
PROCESS_INFORMATION processInfo;
memset( &processInfo, 0, sizeof(processInfo) );
std::cout << "Start [" << cmdline << "]" << std::endl;
if ( CreateProcessA( 0, &cl[0], 0, 0, false,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
rawEnvVars, 0, &startupInfo, &processInfo ) )
{
//CloseHandle( stdoutWriteHandle );
DWORD wordsRead;
char tBuf[257] = {'\0'};
bool success = true;
std::string outBuf("");
unsigned int t;
while(success) {
//__asm _emit 0xcc;
std::cout << "Just before ReadFile(...)" << std::endl;
success = ReadFile( stdoutReadHandle, tBuf, 256, &wordsRead, NULL);
(t=GetLastError())?print_error(t):t=t;
std::cout << "Just after ReadFile(...) | read " << wordsRead<< std::endl;
std::cout << ".";
if( success == false ) break;
outBuf += tBuf;
tBuf[0] = '\0';
}
std::cout << "output = [" << outBuf << "]" << std::endl;
if ( WaitForSingleObject( processInfo.hProcess, INFINITE ) == WAIT_OBJECT_0 )
{
unsigned int exitcode = 0;
GetExitCodeProcess( processInfo.hProcess, (LPDWORD)&exitcode );
std::cout << "exitcode = [" << exitcode << "]" << std::endl;
//__asm _emit 0xcc;
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return exitcode;
}
}
else
{
DWORD procErr = GetLastError();
std::cout << "FAILED TO CREATE PROCESS!" << std::endl;
print_error( procErr );
}
return -1;
} // end launch()
#包括“stdafx.h”
#包括
#包括
未签名int启动(const std::string和cmdline);
int _tmain(int argc,_TCHAR*argv[]
{
启动(std::string(“C:/windows/system32/help.exe”);
返回0;
}
无效打印错误(无符号整数错误)
{
char*msg=NULL;
格式化消息a(
格式化消息分配缓冲区
格式化来自\u系统的\u消息\u|
格式化\u消息\u忽略\u插入,
无效的
犯错误
MAKELANGID(LANG_中立,SUBLANG_默认),
(LPSTR)和msg,
0,空);
std::coutReadFile()中有一个“LPOVERLAPPED LPOVERLAPPED”参数,您已将其设置为NULL。看起来唯一的方法是允许管道上有重叠的I/O,然后对“overlapped.hEvent”使用WaitForSingleObject()
另一种方法是使用函数并为管道创建重叠结构。您的代码中有一些错误,但最重要的是您为bInheritHandles
参数指定了FALSE
,以CreateProcess
。如果新进程没有继承管道的句柄,它将无法使用管道。In为了继承句柄,bInheritHandles
参数必须为TRUE
,并且句柄必须启用继承
其他问题:
- 您正在指定
CREATE\u UNICODE\u ENVIRONMENT
但要传递ANSI环境块。请注意,传递lpEnvironment
的NULL
更容易,让系统为您复制环境块。在这种情况下,您仍应指定CREATE\u UNICODE\u ENVIRONMENT
,如文档中所述因为您的环境块可能包含Unicode字符
- 类似地,如果调用CreateProcessA,则应该使用STARTUPINFOA
- 在循环中,您不会每次都以零终止
tBuf
,因此在输出缓冲区中会得到虚假的额外字符
- 您需要在进入读取循环之前关闭stdoutWriteHandle,否则您将不知道子流程何时退出。(或者您可以使用异步IO并显式检查流程是否退出。)
- 如果API函数成功,
GetLastError()
是未定义的,因此只有在ReadFile
返回FALSE
时才应该调用它(当然,在这种情况下,这纯粹是装饰性的,因为您没有对错误代码执行操作)
作为参考,这里是我对您的代码的更正版本。我已经将其转换为纯C(抱歉!),因为这是我熟悉的。我在Unicode模式下编译和测试,但我认为它在ANSI模式下也应该可以工作,无需修改
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <windows.h>
#include <stdio.h>
void launch(const char * cmdline_in)
{
PROCESS_INFORMATION processInfo;
STARTUPINFOA startupInfo;
SECURITY_ATTRIBUTES saAttr;
HANDLE stdoutReadHandle = NULL;
HANDLE stdoutWriteHandle = NULL;
char cmdline[256];
char outbuf[32768];
DWORD bytes_read;
char tBuf[257];
DWORD exitcode;
strcpy_s(cmdline, sizeof(cmdline), cmdline_in);
memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000))
{
printf("CreatePipe: %u\n", GetLastError());
return;
}
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
{
printf("SetHandleInformation: %u\n", GetLastError());
return;
}
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdError = stdoutWriteHandle;
startupInfo.hStdOutput = stdoutWriteHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// memset(&processInfo, 0, sizeof(processInfo)); // Not actually necessary
printf("Starting.\n");
if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, NULL, 0, &startupInfo, &processInfo))
{
printf("CreateProcessA: %u\n", GetLastError());
return;
}
CloseHandle(stdoutWriteHandle);
strcpy_s(outbuf, sizeof(outbuf), "");
for (;;) {
printf("Just before ReadFile(...)\n");
if (!ReadFile(stdoutReadHandle, tBuf, 256, &bytes_read, NULL))
{
printf("ReadFile: %u\n", GetLastError());
break;
}
printf("Just after ReadFile, read %u byte(s)\n", bytes_read);
if (bytes_read > 0)
{
tBuf[bytes_read] = '\0';
strcat_s(outbuf, sizeof(outbuf), tBuf);
}
}
printf("Output: %s\n", outbuf);
if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
{
printf("WaitForSingleObject: %u\n", GetLastError());
return;
}
if (!GetExitCodeProcess(processInfo.hProcess, &exitcode))
{
printf("GetExitCodeProcess: %u\n", GetLastError());
return;
}
printf("Exit code: %u\n", exitcode);
CloseHandle( processInfo.hProcess );
CloseHandle( processInfo.hThread );
return;
}
int main(int argc, char** argv)
{
launch("C:\\windows\\system32\\help.exe");
return 0;
}
#定义WIN32_WINNT WIN32_WINNT_WIN7
#包括
#包括
无效启动(const char*cmdline_in)
{
处理信息处理信息;
STARTUPINFOA startupInfo;
安全属性saAttr;
句柄stdouredHandle=NULL;
句柄stdoutWriteHandle=NULL;
字符cmdline[256];
煤焦突出[32768];
DWORD字节的读取;
char-tBuf[257];
德沃德出口代码;
strcpy_s(cmdline、sizeof(cmdline)、cmdline_in);
memset(&saAttr,0,sizeof(saAttr));
saAttr.nLength=sizeof(安全属性);
saAttr.bInheritHandle=TRUE;
saAttr.lpSecurityDescriptor=NULL;
//为子进程的标准输出创建管道。
if(!CreatePipe(&stdoutradhandle,&stdoutWriteHandle,&saAttr,5000))
{
printf(“CreatePipe:%u\n”,GetLastError());
返回;
}
//确保未继承STDOUT管道的读取句柄。
if(!SetHandleInformation(stdouredHandle,句柄\标志\继承,0))
{
printf(“SetHandleInformation:%u\n”,GetLastError());
返回;
}
memset(&startupInfo,0,sizeof(startupInfo));
startupInfo.cb=sizeof(startupInfo);
startupInfo.hStdError=stdoutWriteHandle;
startupInfo.hstOutput=stdoutWriteHandle;
startupInfo.hStdInput=GetStdHandle(标准输入句柄);
startupInfo.dwFlags |=STARTF_USESTDHANDLES;
//memset(&processInfo,0,sizeof(processInfo));//实际上不需要
printf(“开始。\n”);
如果(!CreateProcessA(NULL,cmdline,NULL,NULL,TRUE,
创建_NO_窗口|创建_UNICODE_环境,NULL,0,&startupInfo,&processInfo))
{
printf(“CreateProcessA:%u\n”,GetLastError());
返回;
}
CloseHandle(stdoutWriteHandle);
strcpy_s(突发事件,突发事件),sizeof(突发事件),;
对于(;;){
printf(“就在ReadFile(…)\n)之前”;
if(!ReadFile(stdouredHandle,tBuf,256,&bytes\u read,NULL))
{
printf(“读取文件:%u\n”,GetLastError());
打破
}
printf(“就在ReadFile之后,读取%u字节,\n”,字节\u读取);
如果(字节读取>0)
{
tBuf[bytes_read]='\0';
strcat_s(突发事件、规模(突发事件)、tBuf);
}
}
printf(“输出:%s\n”,exputf);
if(WaitForSingleObject(processInfo.hProcess,无穷大)!=WAIT\u OBJECT\u 0)
{
printf(“WaitForSingleObject:%u\n”,GetLastError());
返回;
}
if(!GetExitCodeProcess(processInfo.hProcess,&exitcode))
{
printf(“GetExitCodeProcess:%u\n”,GetLastError());
返回;
}
printf(“退出代码:%u\n”,退出代码);