创建具有重定向输入和输出的子进程

【勇芳软件工作室】汉化HomePreviousNext

本主题中的示例演示了如何从控制台进程创建子进程。它还演示了一种使用匿名管道重定向子进程的标准输入和输出句柄的技术。

CreatePipe函数使用SECURITY_ATTRIBUTES结构为两个管道的读取和写入端创建可继承的句柄。一个管道的读取端作为子进程的标准输入,另一个管道的写入端是子进程的标准输出。这些管道手柄在SetStdHandle函数中指定,这使得它们成为子进程继承的标准句柄。创建子进程后,再次使用SetStdHandle恢复父进程的原始标准句柄。

父进程使用管道的另一端写入子进程的输入并读取子进程的输出。管道这些端部的手柄也是可继承的。但是,该句柄不能被继承。在创建子进程之前,父进程必须使用DuplicateHandle创建不能继承的应用程序定义的hChildStdinWr全局变量的副本。然后使用CloseHandle关闭可继承句柄。有关详细信息,请参阅管道.

以下是父进程。

#include < stdio.h >

#include < windows.h >

// The steps for redirecting child process's STDOUT:

HANDLE hChildStdinRd,hChildStdinWr,hChildStdinWrDup,

hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup

hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID);

VOID WriteToPipe(VOID);

VOID ReadFromPipe(VOID);

VOID ErrorExit(LPTSTR);

VOID ErrMsg(LPTSTR, BOOL);

DWORD main(int argc,char * argv [])

{

SECURITY_ATTRIBUTES saAttr;

BOOL fSuccess;

//设置bInheritHandle标志,以便管道句柄被继承。

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

saAttr.bInheritHandle = TRUE;

saAttr.lpSecurityDescriptor = NULL;

//重定向子进程STDOUT的步骤:

// 1.保存当前STDOUT,稍后恢复。

// 2.创建用于子进程的匿名管道作为STDOUT。

// 3.将父进程的STDOUT设置为写入句柄

//管道,所以它由子进程继承。

// 4.创建读取句柄的不可复制的副本

//关闭可继承的读取句柄。

//将句柄保存到当前STDOUT。

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

//为子进程的STDOUT创建一个管道。

if(!CreatePipe(& hChildStdoutRd,& hChildStdoutWr,& saAttr,0))

ErrorExit("Stdout pipe creation failed\n");

//将管道的写入句柄设置为STDOUT。

if(!SetStdHandle(STD_OUTPUT_HANDLE,hChildStdoutWr))

ErrorExit("Redirecting STDOUT failed");

//创建不可读的读取句柄并关闭可继承的读取

//句柄。

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,

GetCurrentProcess(),{989 796 005} hChildStdoutRdDup,0,

FALSE,

DUPLICATE_SAME_ACCESS);

如果(!fSuccess)

ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdoutRd);

//重定向子进程的STDIN步骤:

// 1.保存当前STDIN,稍后恢复。

// 2.创建匿名管道作为子进程的STDIN。

// 3.将父级的STDIN设置为该读取句柄

// pipe,所以它被子进程继承。

// 4.创建写入句柄的不可复制的副本,

//并关闭可继承的写入句柄。

//将句柄保存到当前STDIN。

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

//为子进程的STDIN创建一个管道。

if(!CreatePipe(& hChildStdinRd,& hChildStdinWr,& saAttr,0))

ErrorExit("Stdin pipe creation failed\n");

//将管道的读取句柄设置为STDIN。

if(!SetStdHandle(STD_INPUT_HANDLE,hChildStdinRd))

ErrorExit("Redirecting Stdin failed");

//将写入句柄复制到管道,以使其不被继承。

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,

GetCurrentProcess(),{989 796 005} hChildStdinWrDup,0,

FALSE, //不继承

DUPLICATE_SAME_ACCESS);

如果(!fSuccess)

ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdinWr);

//现在创建子进程。

if(!CreateChildProcess())

ErrorExit("Create process failed");

//进程创建后,恢复保存的STDIN和STDOUT。

if(!SetStdHandle(STD_INPUT_HANDLE,hSaveStdin))

ErrorExit("Re-redirecting Stdin failed\n");

if(!SetStdHandle(STD_OUTPUT_HANDLE,hSaveStdout))

ErrorExit("Re-redirecting Stdout failed\n");

//获取父输入文件的句柄。

if(argc > 1)

hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

其他

hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)

ErrorExit("no input file\n");

//写入作为子进程的标准输入的管道。

WriteToPipe();

//读取作为子进程的标准输出的管道。

ReadFromPipe();

return 0;

}

BOOL CreateChildProcess()

{

PROCESS_INFORMATION piProcInfo;

STARTUPINFO siStartInfo;

//设置STARTUPINFO结构的成员。

ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );

siStartInfo.cb = sizeof(STARTUPINFO);

//创建子进程。

返回CreateProcess(NULL,

"child", // 命令行

NULL, //进程安全属性

NULL, //主线程安全属性

TRUE, //句柄被继承

0, //创建标志

NULL, //使用父级的环境

NULL, //使用父目录

&siStartInfo, // STARTUPINFO指针

&piProcInfo); //接收PROCESS_INFORMATION

}

VOID WriteToPipe(VOID)

{

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

//从文件读取并将其内容写入管道。

for (;;)

{

if(!ReadFile(hInputFile,chBuf,BUFSIZE,& dwRead,NULL)||

dwRead == 0) break;

if(!WriteFile(hChildStdinWrDup,chBuf,dwRead,

&dwWritten, NULL)) break;

}

//关闭管道手柄,以便子进程停止读取。

if(!CloseHandle(hChildStdinWrDup))

ErrorExit("Close pipe failed\n");

}

VOID ReadFromPipe(VOID)

{

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

//在读取之前关闭管道的写入端

//读取管道的一端。

if(!CloseHandle(hChildStdoutWr))

ErrorExit("Closing handle failed");

//从子进程读取输出,并写入父级的STDOUT。

for (;;)

{

if(!ReadFile(hChildStdoutRdDup,chBuf,BUFSIZE,& dwRead,

NULL) || dwRead == 0) break;

if(!WriteFile(hSaveStdout,chBuf,dwRead,& dwWritten,NULL))

break;

}

}

VOID ErrorExit(LPTSTR lpszMessage)

{

fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);

}

//子进程的代码。

#include < windows.h >

// The steps for redirecting child process's STDOUT:

VOID主(VOID)

{

CHAR chBuf[BUFSIZE];

DWORD dwRead, dwWritten;

HANDLE hStdin, hStdout;

BOOL fSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

hStdin = GetStdHandle(STD_INPUT_HANDLE);

if ((hStdout == INVALID_HANDLE_VALUE) ||

(hStdin == INVALID_HANDLE_VALUE))

ExitProcess(1);

for (;;)

{

//从标准输入读取。

fSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

if (! fSuccess || dwRead == 0)

break;

//写入标准输出。

fSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

如果(!fSuccess)

break;

}

}