如何启动流程并获取其输出?

在使用Windows API的C语言中,如何在获取进程信息时获取进程的输出?

我有这样的代码:

STARTUPINFO si1; ZeroMemory(&si1,sizeof(si1)); PROCESS_INFORMATION pi1; ZeroMemory(&pi1,sizeof(pi1)); BOOL bRes1=CreateProcess(_T("C:\\User\\asd.exe"),cmd_line1,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL, &si1,&pi1); 

并且进程asd.exe打印出一定的输出,我想把它带到我的进程(我使用上面的代码)。

这个答案可能比您预期的要长一些,但这就是Windows API的有时候。 在任何情况下,即使这需要比最初看起来需要的代码更多的代码,这至少为想要做这样的事情的程序提供了一个相当干净,易于使用的界面。 代码被公平地评论,不仅解释了如何使用它提供的function,还解释了它如何使用它所做的大部分工作,以及如果您决定基于此而不是直接使用它来编写代码,Windows需要什么。

我还应该指出,这是一些相当古老的代码。 它运行得很好,我没有任何理由重写它,但如果我今天再次这样做,我很确定我会做的有点不同(一方面,我无疑使用C ++而不是直C,就像我在这里做的那样)。

这也包含一些经常用于完全不相关的代码的花絮(例如,我在很多地方使用了system_error – 它只是Windows,但实际上是产生子进程的偶然事件)。

无论如何,我们将从spawn.h开始,它定义了代码的接口:

 #ifndef SPAWN_H_INCLUDED_ #define SPAWN_H_INCLUDED_ // What to do if you ask to create a file and it already exists. // We can fail to create it, overwrite the existing content, or append the // new content to the existing content. enum { FAIL, OVERWRITE, APPEND }; // This just specifies the type of a thread procedure to use to handle a stream // to/from the child, if you decided to do that. // typedef unsigned long (__stdcall *ThrdProc)(void *); // stream_info is the real core of the code. It's what lets you specify how // to deal with a particular stream. When you call CreateDetchedProcess, // you need to pass the address of an array of three stream_info objects // that specify the handling for the child's standard input, standard // output, and standard error streams respectively. If you specify a // filename, that stream will be connected to the named file. If you set // filename to NULL, you can instead specify a procedure that will be // started in a thread that will provide data for that stream, or process // the data coming from that stream. Toward the bottom of spawn.c there are // a couple of sample handlers, one that processes standard error, and the // other that processes standard output from a spawned child process. // typedef struct { char *filename; ThrdProc handler; HANDLE handle; } stream_info; // Once you've filled in your stream_info structures, spawning the child is // pretty easy: just pass the name of the executable for the child, and the // address of the stream_info array. This handles most of the usual things: // if you don't specify an extension for the file, it'll search for it with // extensions of `.com", ".exe", ".cmd", and ".bat" in the current // directory, and then in any directory specified by the PATH environment // variable. It'll open/create any files you've specified in the // stream_info structures, and create pipes for any streams that are to be // directed to the parent, and start up threads to run any stream handlers // specified. // HANDLE CreateDetachedProcess(char const *name, stream_info *streams); #endif 

然后执行CreateDetachedProcess (以及一些测试/演示代码):

 #define STRICT #define WIN32_LEAN_AND_MEAN #include  #include  #include  #include  #include  #include  #include "spawn.h" static void system_error(char const *name) { // A function to retrieve, format, and print out a message from the // last error. The `name' that's passed should be in the form of a // present tense noun (phrase) such as "opening file". // char *ptr = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, (char *)&ptr, 1024, NULL); fprintf(stderr, "%s\n", ptr); LocalFree(ptr); } static void InitializeInheritableSA(SECURITY_ATTRIBUTES *sa) { sa->nLength = sizeof *sa; sa->bInheritHandle = TRUE; sa->lpSecurityDescriptor = NULL; } static HANDLE OpenInheritableFile(char const *name) { SECURITY_ATTRIBUTES sa; HANDLE retval; InitializeInheritableSA(&sa); retval = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (INVALID_HANDLE_VALUE == retval) { char buffer[100]; sprintf(buffer, "opening file %s", name); system_error(buffer); return retval; } } static HANDLE CreateInheritableFile(char const *name, int mode) { SECURITY_ATTRIBUTES sa; HANDLE retval; DWORD FSmode = mode ? OPEN_ALWAYS : CREATE_NEW; InitializeInheritableSA(&sa); retval = CreateFile( name, GENERIC_WRITE, FILE_SHARE_READ, &sa, FSmode, FILE_ATTRIBUTE_NORMAL, 0); if (INVALID_HANDLE_VALUE == retval) { char buffer[100]; sprintf(buffer, "creating file %s", name); system_error(buffer); return retval; } if ( mode == APPEND ) SetFilePointer(retval, 0, 0, FILE_END); } enum inheritance { inherit_read = 1, inherit_write = 2 }; static BOOL CreateInheritablePipe(HANDLE *read, HANDLE *write, int inheritance) { SECURITY_ATTRIBUTES sa; InitializeInheritableSA(&sa); if ( !CreatePipe(read, write, &sa, 0)) { system_error("Creating pipe"); return FALSE; } if (!inheritance & inherit_read) DuplicateHandle( GetCurrentProcess(), *read, GetCurrentProcess(), NULL, 0, FALSE, DUPLICATE_SAME_ACCESS); if (!inheritance & inherit_write) DuplicateHandle( GetCurrentProcess(), *write, GetCurrentProcess(), NULL, 0, FALSE, DUPLICATE_SAME_ACCESS); return TRUE; } static BOOL find_image(char const *name, char *buffer) { // Try to find an image file named by the user. // First search for the exact file name in the current // directory. If that's found, look for same base name // with ".com", ".exe" and ".bat" appended, in that order. // If we can't find it in the current directory, repeat // the entire process on directories specified in the // PATH environment variable. // #define elements(array) (sizeof(array)/sizeof(array[0])) static char *extensions[] = {".com", ".exe", ".bat", ".cmd"}; int i; char temp[FILENAME_MAX]; if (-1 != access(name, 0)) { strcpy(buffer, name); return TRUE; } for (i=0; i 

据我所知,你正在使用Windows(因为你提到过程信息)。 如果要获取已启动的进程输出,则必须捕获其输出流。 这是一个解释性的链接 ,它告诉我们如何做到这一点。

MY2C