Creating a Child Process with Redirected Input and Output
The example in this topic demonstrates how to create a child process using the CreateProcess function from a console process. It also demonstrates a technique for using anonymous pipes to redirect the child process's standard input and output handles. Note that named pipes can also be used to redirect process I/O.
The CreatePipe function uses the SECURITY_ATTRIBUTES structure to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the standard output for the child process. These pipe handles are specified in the STARTUPINFO structure, which makes them the standard handles inherited by the child process.
The parent process uses the opposite ends of these two pipes to write to the child process's input and read from the child process's output. As specified in the STARTUPINFO structure, these handles are also inheritable. However, these handles must not be inherited. Therefore, before creating the child process, the parent process uses the SetHandleInformation function to ensure that the write handle for the child process's standard input and the read handle for the child process's standard input cannot be inherited. For more information, see Pipes.
The following is the code for the parent process. It takes a single command-line argument: the name of a text file.
#include <windows.h> #include <tchar.h> #include <stdio.h> #include <strsafe.h> #define BUFSIZE 4096 HANDLE g_hChildStd_IN_Rd = NULL; HANDLE g_hChildStd_IN_Wr = NULL; HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; HANDLE g_hInputFile = NULL; void CreateChildProcess(void); void WriteToPipe(void); void ReadFromPipe(void); void ErrorExit(PTSTR); int _tmain(int argc, TCHAR *argv[]) { SECURITY_ATTRIBUTES saAttr; printf("\n->Start of parent execution.\n"); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDOUT. if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) ErrorExit(TEXT("StdoutRd CreatePipe")); // Ensure the read handle to the pipe for STDOUT is not inherited. if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) ErrorExit(TEXT("Stdout SetHandleInformation")); // Create a pipe for the child process's STDIN. if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) ErrorExit(TEXT("Stdin CreatePipe")); // Ensure the write handle to the pipe for STDIN is not inherited. if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) ) ErrorExit(TEXT("Stdin SetHandleInformation")); // Create the child process. CreateChildProcess(); // Get a handle to an input file for the parent. // This example assumes a plain text file and uses string output to verify data flow. if (argc == 1) ErrorExit(TEXT("Please specify an input file.\n")); g_hInputFile = CreateFile( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if ( g_hInputFile == INVALID_HANDLE_VALUE ) ErrorExit(TEXT("CreateFile")); // Write to the pipe that is the standard input for a child process. // Data is written to the pipe's buffers, so it is not necessary to wait // until the child process is running before writing data. WriteToPipe(); printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]); // Read from pipe that is the standard output for child process. printf( "\n->Contents of child process STDOUT:\n\n", argv[1]); ReadFromPipe(); printf("\n->End of parent execution.\n"); // The remaining open handles are cleaned up when this process terminates. // To avoid resource leaks in a larger application, close handles explicitly. return 0; } void CreateChildProcess() // Create a child process that uses the previously created pipes for STDIN and STDOUT. { TCHAR szCmdline[]=TEXT("child"); PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; // Set up members of the PROCESS_INFORMATION structure. ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = g_hChildStd_OUT_Wr; siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; siStartInfo.hStdInput = g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. bSuccess = CreateProcess(NULL, szCmdline, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION // If an error occurs, exit the application. if ( ! bSuccess ) ErrorExit(TEXT("CreateProcess")); else { // Close handles to the child process and its primary thread. // Some applications might keep these handles to monitor the status // of the child process, for example. CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); } } void WriteToPipe(void) // Read from a file and write its contents to the pipe for the child's STDIN. // Stop when there is no more data. { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; for (;;) { bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL); if ( ! bSuccess || dwRead == 0 ) break; bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); if ( ! bSuccess ) break; } // Close the pipe handle so the child process stops reading. if ( ! CloseHandle(g_hChildStd_IN_Wr) ) ErrorExit(TEXT("StdInWr CloseHandle")); } void ReadFromPipe(void) // Read output from the child process's pipe for STDOUT // and write to the parent process's pipe for STDOUT. // Stop when there is no more data. { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); for (;;) { bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); if( ! bSuccess || dwRead == 0 ) break; bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL); if (! bSuccess ) break; } } void ErrorExit(PTSTR lpszFunction) // Format a readable error message, display a message box, // and exit from the application. { LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); ExitProcess(1); }
The following is the code for the child process. It uses the inherited handles for STDIN and STDOUT to access the pipe created by the parent. The parent process reads from its input file and writes the information to a pipe. The child receives text through the pipe using STDIN and writes to the pipe using STDOUT. The parent reads from the read end of the pipe and displays the information to its STDOUT.
#include <windows.h> #include <stdio.h> #define BUFSIZE 4096 int main(void) { CHAR chBuf[BUFSIZE]; DWORD dwRead, dwWritten; HANDLE hStdin, hStdout; BOOL bSuccess; hStdout = GetStdHandle(STD_OUTPUT_HANDLE); hStdin = GetStdHandle(STD_INPUT_HANDLE); if ( (hStdout == INVALID_HANDLE_VALUE) || (hStdin == INVALID_HANDLE_VALUE) ) ExitProcess(1); // Send something to this process's stdout using printf. printf("\n ** This is a message from the child process. ** \n"); // This simple algorithm uses the existence of the pipes to control execution. // It relies on the pipe buffers to ensure that no data is lost. // Larger applications would use more advanced process control. for (;;) { // Read from standard input and stop on error or no data. bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL); if (! bSuccess || dwRead == 0) break; // Write to standard output and stop on error. bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL); if (! bSuccess) break; } return 0; }
Send comments about this topic to Microsoft
Build date: 3/7/2012
Further to my previous post, this example, exactly as it is written here, does not really work.
1. The parent does not terminate.
2. The message from the child by printf("\n ** This is a message from the child process. ** \n") appears at the parent AFTER the contents of the file (which is output by the child BEFORE the message)! So, it would seem that printf(...) is actually writing to a different stream than WriteFile(hStdout,...).
What on earth is going on here?
I am running this on Windows7 (64), using Visual Studio 10.
- 4/6/2012
- timgclark
If the parent program wants to fully control the child program, this does NOT work - it just hangs up.
That is, the parent wants to behave like a human would if the child program was simply run in a console, seeing output, typing in corresponding input, ... and so on.
It's obvious that cmd.exe does this correcly - the question is HOW DOES IT DO IT???
This is essential for creating software that uses third-party command line tools that require interaction in a programmatic way. For example, I am creating Automatic Test software for testing electronic components; the tools from the semiconductor manufacturers are often of a command-line variety.
PLEASE - can someone at Microsoft tell us how to do this so that it works?
- 4/6/2012
- timgclark
http://support.microsoft.com/kb/315939 -
PRB: Child Inherits Unintended Handles During CreateProcess Call
This also doesn't fix in all sutialtion, because (eg. third party) dll's also can start child processes, and somethimes you may not be able to protect it by critical section (eg. if dll starts child processes in separate thread).
Not a really big problem for most applications, but this should be noticed. Also thisproblem is workarounded/fixed in Vista+ by using STARTUPINFOEX where you can add list of inherited handled to lpAttributeList (but your whole app/dlls must use this)
- 5/23/2010
- Krystian Bigaj
- 6/6/2010
- Thomas Lee
When started on a console (cmd.exe) it behaves just fine. But when redirected, using pipes, the child process *itself* buffers its output until either the buffer is full, in which case all buffered content is written out in one large chunk or the process exits, in which case closing stdout implies a fflush.
This is beacuse the MS C-Runtime (and 99% of all the programs out there use that) does something like:
DWORD isdev = GetFileType(hStdout);
if (isdev == FILE_TYPE_CHAR)
// Handle is from a console or a printer -> don't buffer output
else if (isdev == FILE_TYPE_PIPE)
// Handle is not from a console or a printer -> do buffer output
The trick would be now to create a pipe handle (or any other handle, for that matter) that has FILE_TYPE_CHAR.
If anyone knows how to do that, I would be more than happy to hear it!
- 11/4/2009
- WKleinschmit
- 11/5/2009
- WKleinschmit
(Follows my previous post)
With some experiments, I found the output is buffered at child side's STDOUT with every 4096 bytes. So, I add a "fflush(stdout)" after printf(), the output redirect to parent without problem then.
BreadPan 3/26/2009
- 3/26/2009
- breadpan
Hi All,
Same thing LB encountered happens on me. For test purpose, I even write a very simple DOS program as below and built by MINGW gcc to be the child process.
#include <windows.h>
main(void)
{
printf("Start test.c");
Sleep(10000); //Wait 10 seconds
}
Parent side receives the printf string after 10 seconds while the child terminates itself.
However, if I replace the child with "ping 127.0.0.1 -t", parent continuously receives the ping result every second as expected. Maybe someone can shed the light on this.
BreadPan