匿名管道实现获取控制台程序输出
在程序设计中,有时候需要调用一些控制台程序并取得其在控制台上的输出,如VS的IDE就调用了cl.exe、link.exe等控制台的程序,并可将这些程序的输出在IDE中显示出来。
曾经很迷惑这个功能的实现,直到有一天看到下面的参考代码才恍然大悟,原来一切都这么简单,只不过用了CreatePipe、CreateProcess、ReadFile寥寥几个函数而已。特此记下以供参考。
#include"Stdafx.h"
#include<windows.h>
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
/* protos */
DWORD WINAPI ReadFromPipe(LPVOID args);
/* globals */
#define CHUNK 25
#define STATICBUFFERSIZE 1000
typedefstruct {
HANDLE pipe;
char buffer[STATICBUFFERSIZE];
} pipeinfo;
pipeinfo Out = {INVALID_HANDLE_VALUE, '/0'};
pipeinfo Err = {INVALID_HANDLE_VALUE, '/0'};
int main( int argc, char *argv[])
{
if (argc < 2)
{
printf("pipetest ??");
return 0;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
DWORD threadID;
char msg[300];
BOOL ok;
HANDLE hProcess, h, pipeThreads[2];
char cmdline[100];
hProcess = GetCurrentProcess();
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = INVALID_HANDLE_VALUE;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
/* Create a non-inheritible pipe. */
CreatePipe(&Out.pipe, &h, &sa, 0);
/* Dupe the write side, make it inheritible, and close the original. */
DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
/* Same as above, but for the error side. */
CreatePipe(&Err.pipe, &h, &sa, 0);
DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
strcpy(cmdline, "");
for (int i = 1; i < argc; i++)
{
strcat(cmdline, argv[i]);
strcat(cmdline, " ");
}
ok = CreateProcess(NULL, cmdline, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi);
if (!ok)
{
DWORD err = GetLastError();
int chars = _snprintf(msg, sizeof(msg) - 1, "Tried to launch: /"%s/", but got error [%u]: ", cmdline, err);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L , err, 0, &msg[chars], (300-chars), 0);
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
return 2;
}
/* Close our references to the write handles that have now been inherited.*/
CloseHandle(si.hStdOutput);
CloseHandle(si.hStdError);
WaitForInputIdle(pi.hProcess, 5000);
CloseHandle(pi.hThread);
/* Start the pipe reader threads.*/
pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
/* Block waiting for the process to end.*/
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
/* Wait for our pipe to get done reading, should it be a little slow.*/
WaitForMultipleObjects(2, pipeThreads, TRUE, INFINITE);
CloseHandle(pipeThreads[0]);
CloseHandle(pipeThreads[1]);
return 0;
}
DWORD WINAPI ReadFromPipe(LPVOID args)
{
pipeinfo *pi = (pipeinfo *) args;
char *lastBuf = pi->buffer;
DWORD dwRead;
BOOL ok;
char buf[2000];
again:
ok = ReadFile(pi->pipe, buf, 2000, &dwRead, 0L );
if (!ok || dwRead == 0)
{
CloseHandle(pi->pipe);
return 0;
}
else
{
buf[dwRead] = 0;
printf("%s", buf);
}
goto again:
return 0; /* makes the compiler happy */
}