The following example shows the creation of a child process by a console application and illustrates a technique for using anonymous pipes to redirect the Standard input and output of the child process. If you have a program that can read its input from Stdin or write its results to Stdout, redirected I/O allows you to execute the program as a child process while controlling its input and receiving its output.
The example also shows how to create handles that can be inherited, as well as some of the problems that can arise from inherited handles. All of the functions that create inheritable handles, including the CreatePipe function used by this program, have a SECURITY_ATTRIBUTES parameter whose bInheritHandle flag must be set to TRUE for the handle to be inherited. In addition, the CreateProcess function has an fInheritHandles flag that must also be set to TRUE for any handles to be inherited. The inheritance problems in this program arise from the fact that CreatePipe returns inheritable handles to both the read and the write ends of each pipe created. In the case of the Stdin pipe, you want the read end to be inherited but not the write end. The reason for this is that the child process will read from the pipe until it reads an EOF which is sent when the last handle to the write end of the pipe is closed. But when the child inherits an open handle to the write end of its Stdin pipe, it will block indefinitely waiting for the EOF. So the parent must use DuplicateHandle to create a duplicate, non-inheritable handle to the write end, and then close the inheritable handle. Similarly, the parent must close the write handle of the Stdout pipe. This is done after the child process has been created (so it can inherit the handle), but before the parent process begins to read from the pipe.
Parent process:
#include <stdio.h>
#include <windows.h>
#define BUFSIZE 4096
HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr,
hInputFile, hSaveStdin, hSaveStdout;
BOOL CreateChildProcess(void);
DWORD WriteToPipe(void);
DWORD ReadFromPipe(void);
VOID ErrorExit(char *);
DWORD main(int argc, char **argv)
{
SECURITY_ATTRIBUTES attr;
/* set SECURITY_ATTRIBUTES so pipe handles are inherited */
attr.nLength = sizeof(SECURITY_ATTRIBUTES);
attr.bInheritHandle = TRUE;
attr.lpSecurityDescriptor = NULL;
/*
* The steps for redirecting child's Stdout:
* 1. Save current Stdout, to be restored later
* 2. Create anonymous pipe to be Stdout for child
* 3. Set Stdout of parent to be write handle of pipe, so
* it is inherited by child.
*/
DuplicateHandle(GetCurrentProcess(),
GetStdHandle(STD_OUTPUT_HANDLE),
GetCurrentProcess(), &hSaveStdout, 0,
FALSE, /* not inherited */
DUPLICATE_SAME_ACCESS);
if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &attr, 0))
ErrorExit("Stdout pipe creation failed\n");
if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
ErrorExit("Redirecting Stdout failed\n");
/*
* The steps for redirecting child's Stdin:
* 1. Save current Stdin, to be restored later
* 2. Create anonymous pipe to be Stdin for child
* 3. Set Stdin of parent to be read handle of pipe, so
* it is inherited by child.
* 4. Create a non-inheritable duplicate of write handle,
* and close the inheritable write handle.
*/
DuplicateHandle(GetCurrentProcess(),
GetStdHandle(STD_INPUT_HANDLE),
GetCurrentProcess(), &hSaveStdin, 0,
FALSE, /* not inherited */
DUPLICATE_SAME_ACCESS);
if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &attr, 0))
ErrorExit("Stdin pipe creation failed\n");
if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
ErrorExit("Redirecting Stdin failed\n");
DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
GetCurrentProcess(), &hChildStdinWrDup, 0,
FALSE, /* not inherited */
DUPLICATE_SAME_ACCESS);
CloseHandle(hChildStdinWr);
/* now create the child process */
if (!CreateChildProcess())
ErrorExit("Create process failed\n");
/* after process creation, restore saved 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");
/* get a handle to the parent's input file */
if (argc > 1)
hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
else
hInputFile = GetStdHandle(STD_INPUT_HANDLE);
if (hInputFile == INVALID_HANDLE_VALUE)
ErrorExit("no input file\n");
/* write to the pipe that is Standard Input for child process */
WriteToPipe();
/* read from the pipe that is Standard Output for child process */
ReadFromPipe();
return 0;
}
BOOL CreateChildProcess(void)
{
PROCESS_INFORMATION procinfo;
STARTUPINFO startinfo;
/* set up STARTUPINFO structure */
startinfo.cb = sizeof(STARTUPINFO);
startinfo.lpReserved = NULL;
startinfo.lpReserved2 = NULL;
startinfo.cbReserved2 = 0;
startinfo.lpDesktop = NULL;
startinfo.lpTitle = NULL;
startinfo.dwFlags = 0;
return CreateProcess(NULL,
"child", /* command line */
NULL, /* process attrs */
NULL, /* primary thread attrs */
TRUE, /* handles inherited */
0, /* creation flags */
NULL, /* use parent's environment */
NULL, /* use parent's current directory */
&startinfo, /* address of STARTUPINFO */
&procinfo); /* address of PROCESS_INFORMATION */
}
DWORD WriteToPipe(void)
{
DWORD dwRead, dwWritten;
char buf[BUFSIZE];
for (;;) {
if (!ReadFile(hInputFile, buf, BUFSIZE, &dwRead, NULL) ||
!dwRead)
break;
if (!WriteFile(hChildStdinWrDup, buf, dwRead,
&dwWritten, NULL))
break;
}
if (!CloseHandle(hChildStdinWrDup))
ErrorExit("Close pipe failed\n");
return 0;
}
DWORD ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
char buf[BUFSIZE];
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
/* Close write end of pipe before reading from read end of pipe. */
if (!CloseHandle(hChildStdoutWr))
ErrorExit("Redirecting Stdout failed\n");
for (;;) {
if (! ReadFile(hChildStdoutRd, buf, BUFSIZE, &dwRead, NULL) ||
!dwRead)
break;
if (!WriteFile(hStdout, buf, dwRead, &dwWritten, NULL))
break;
}
return 0;
}
VOID ErrorExit(char *message)
{
fprintf(stderr, message);
ExitProcess(0);
}
Child process:
#include <windows.h>
#define BUFSIZE 4096
DWORD main(void)
{
char buf[BUFSIZE];
DWORD dwRead, dwWritten;
HANDLE hStdin, hStdout;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if ((hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE))
return 1;
for (;;) {
if (!ReadFile(hStdin, buf, BUFSIZE, &dwRead, NULL) ||
!dwRead) break;
if (!WriteFile(hStdout, buf, dwRead, &dwWritten, NULL))
break;
}
return 0;
}