The Windows pipe, child process and redirection C program example

 

 

Compiler: Visual C++ Express Edition 2005

Compiled on Platform: Windows XP Pro SP2

Target platform: none, just for learning and fun

Header file: Standard and Windows

Additional library: Windows Platform SDK

Additional project setting: Set project to be compiled as C

Project -> your_project_name Properties -> Configuration Properties -> C/C++ -> Advanced -> Compiled As: Compiled as C Code (/TC)

Other info: non-CLR or unmanaged. Need to add netapi32.lib (netapi32.dll) to the project. Click the Project menu->Select the your_project_name Properties... sub menu->Expand the Configuration Properties folder on the left pane->Expand the Linker subfolder->Select the Input subfolder->Select the Additional Dependencies field on the right pane->Click the ... at the end of the field->Type in 'netapi32.lib' in the empty pane->Click the OK button->Click the OK button second time to close the project Properties dialog.

To do: Windows pipe, redirection and creating a child process

To show: The various Windows process, thread and pipe usage

 

 

 

 

 

 

#include <windows.h>

#include <stdio.h>

#include <strsafe.h>

 

// Constant

#define BUFSIZE 4096

 

// Global variables for standard child read/write handle

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;

 

// Function prototypes, needed for C++

void CreateChildProcess(void);

void WriteToPipe(void);

void ReadFromPipe(void);

void ErrorExit(PTSTR);

 

// wmain() is the main process...

int wmain(int argc, WCHAR *argv[])

{

SECURITY_ATTRIBUTES saAttr;

 

// Just some info

wprintf(L"Start of parent execution.\n");

wprintf(L"Parent process ID %u\n", GetCurrentProcessId());

wprintf(L"Parent thread ID %u\n", GetCurrentThreadId());

 

// Set the bInheritHandle flag so pipe handles are inherited

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

saAttr.bInheritHandle = TRUE;

// Just a dummy (point to NULL), we do not implement it

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(L"CreatePipe() - pipe for child process\'s STDOUT failed");

else

wprintf(L"CreatePipe() - pipe for child process\'s STDOUT pipe was created!\n");

 

// Ensure the read handle to the pipe for STDOUT is not inherited

if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))

ErrorExit(L"SetHandleInformation() - pipe STDOUT read handle failed for inheritance");

else

wprintf(L"SetHandleInformation() - pipe STDOUT read handle is not inherited!\n");

 

// Create a pipe for the child process's STDIN

if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))

ErrorExit(L"CreatePipe() - pipe for child\'s STDIN failed");

else

wprintf(L"CreatePipe() - pipe for child process\'s STDIN was created!\n");

 

// Ensure the write handle to the pipe for STDIN is not inherited

if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))

ErrorExit(L"SetHandleInformation() - pipe STDIN write handle failed for inheritance");

else

wprintf(L"Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!\n");

 

// Create the child process

wprintf(L"Verify: argv[1] = %s\n", argv[1]);

wprintf(L"Creating the child process...\n");

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(L"Please specify an input file.\n");

wprintf(L"%s [sample_text_file.txt]\n", argv[0]);

}

 

g_hInputFile = CreateFile(

argv[1],

GENERIC_READ,

0,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_READONLY,

NULL);

 

if(g_hInputFile == INVALID_HANDLE_VALUE)

ErrorExit(L"CreateFile()");

else

wprintf(L"CreateFile() - %s was successfully opened!\n", argv[1]);

 

// 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();

wprintf(L" Contents of %s written to child STDIN pipe.\n", argv[1]);

 

// Read from pipe that is the standard output for child process

wprintf(L" Contents of %s child process STDOUT:\n", argv[1]);

ReadFromPipe();

wprintf(L" 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;

}

 

// Create a child process that uses the previously created pipes for STDIN and STDOUT

void CreateChildProcess()

{

// The following should be the child executable, see the next program example

// Change the path accordingly...

WCHAR szCmdline[]=L"\\\\?\\C:\\amad\\ChildProcess\\Debug\\ChildProcess.exe";

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, // Use szCmdLine

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(L"CreateProcess() - child");

else

{

wprintf(L"\nChild process ID is: %u\n", GetCurrentProcessId());

wprintf(L"Child thread ID is: %u\n", GetCurrentThreadId());

 

// 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

if(CloseHandle(piProcInfo.hProcess) != 0)

wprintf(L"piProcInfo.hProcess handle was closed!\n");

else

ErrorExit(L"CloseHandle(piProcInfo.hProcess)");

 

if(CloseHandle(piProcInfo.hThread) != 0)

wprintf(L"piProcInfo.hThread handle was closed!\n");

else

ErrorExit(L"CloseHandle(piProcInfo.hThread)");

}

}

 

// Read from a file and write its contents to the pipe for the child's STDIN. Stop when there is no more data

void WriteToPipe(void)

{

DWORD dwRead, dwWritten;

CHAR chBuf[BUFSIZE];

BOOL bSuccess = FALSE;

 

for(;;)

{

bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);

if (! bSuccess || dwRead == 0)

{

// Cannot use ErrorExit() lol

wprintf(L"\nReadFile() - Failed to read file! Error %u\n", GetLastError());

break;

}

else

wprintf(L"\nReadFile() - Reading from a file...\n");

 

bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);

if (!bSuccess)

{

wprintf(L"\nWriteFile() - Failed to write to pipe for child\'s STDIN! Error %u\n", GetLastError());

break;

}

else

wprintf(L"\nWriteFile() - writing to the pipe for the child\'s STDIN...\n");

}

 

// Close the pipe handle so the child process stops reading

if (!CloseHandle(g_hChildStd_IN_Wr))

ErrorExit(L"CloseHandle()");

else

wprintf(L"Closing the pipe handle...\n");

}

 

// 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.

void ReadFromPipe(void)

{

DWORD dwRead, dwWritten;

WCHAR chBuf[BUFSIZE];

BOOL bSuccess = FALSE;

HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

 

// Close the write end of the pipe before reading from the read end of the pipe, to control child process execution.

// The pipe is assumed to have enough buffer space to hold the data the child process has already written to it

if (!CloseHandle(g_hChildStd_OUT_Wr))

ErrorExit(L"CloseHandle() - ReadFromPipe()");

for(;;)

{

bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);

if(!bSuccess || dwRead == 0)

{

wprintf(L"\nReadFile() from child's standard output failed! Error %u\n", GetLastError());

break;

}

else

{

wprintf(L"\nReadFile() from child's standard output is OK!\n");

}

 

bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);

if(!bSuccess)

{

wprintf(L"\nWriteFile() to parent's standard output failed! Error %u\n", GetLastError());

break;

}

else

{

wprintf(L"\nWriteFile() to parent's standard output is OK!\n");

}

}

}

 

// Format a readable error message, display a message box, and exit from the application.

void ErrorExit(PTSTR lpszFunction)

{

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(WCHAR));

StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR), L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf);

MessageBox(NULL, (LPCTSTR)lpDisplayBuf, L"Error", MB_OK);

LocalFree(lpMsgBuf);

LocalFree(lpDisplayBuf);

ExitProcess(1);

}

 

Output example: (run at command prompt)

(This program run at the command prompt)

 

C:\amad\buffalo\Debug>buffalo

Start of parent execution.

Parent process ID 3248

Parent thread ID 2492

CreatePipe() - pipe for child process's STDOUT pipe was created!

SetHandleInformation() - pipe STDOUT read handle is not inherited!

CreatePipe() - pipe for child process's STDIN was created!

Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!

Verify: argv[1] = (null)

Creating the child process...

 

Next, we create a text file named justatextfile.txt under the debug folder (same folder as the executable) and rerun the program. We put some text in the file.

 

C:\amad\buffalo\Debug>buffalo justatextfile.txt

Start of parent execution.

Parent process ID 4180

Parent thread ID 836

CreatePipe() - pipe for child process's STDOUT pipe was created!

SetHandleInformation() - pipe STDOUT read handle is not inherited!

CreatePipe() - pipe for child process's STDIN was created!

Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!

Verify: argv[1] = justatextfile.txt

Creating the child process...

 

Both runs generate the following error:

 

CreateProcess() - child failed with error 3: The system cannot find the path specified.

 

Because "\\\\?\\C:\\amad\\ChildProcess\\Debug\\ChildProcess.exe"; cannot be found in this case. We will provide the child executable in another example later or you can provide other process as the child process.

 

For example we create another program that create a process and run the Windows cmd command as the child process. The source code is given below (project name is : ChildProcess).

 

// For WinXp as a target, change accordingly

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

void main(void)

{

STARTUPINFO si;

PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));

si.cb = sizeof(si);

ZeroMemory(&pi, sizeof(pi));

 

// Start the child process.

if (!CreateProcess(L"C:\\WINDOWS\\system32\\cmd.exe", // module name.

NULL, // Command line.

NULL, // Process handle not inheritable.

NULL, // Thread handle not inheritable.

FALSE, // Set handle inheritance to FALSE.

0, // No creation flags.

NULL, // Use parents environment block.

NULL, // Use parents starting directory.

&si, // Pointer to STARTUPINFO structure.

&pi) // Pointer to PROCESS_INFORMATION structure.

)

printf("\nSorry! CreateProcess() failed.\n\n");

else

printf("\nWell, CreateProcess() looks OK.\n\n");

 

// Wait until child process exits (in milliseconds). If INFINITE, the functions time-out interval never elapses except with user or other intervention.

WaitForSingleObject(pi.hProcess, INFINITE);

printf("\n");

// Close process and thread handles.

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

}

 

Then we change the path in the first program to:

 

WCHAR szCmdline[]=L"\\\\?\\C:\\amad\\ChildProcess\\Debug\\ChildProcess.exe";

 

And rerun the first program with the previous text file as an argument. The following is the sample output.

 

C:\amad\buffalo\Debug>buffalo justatextfile.txt

Start of parent execution.

Parent process ID 1144

Parent thread ID 4856

CreatePipe() - pipe for child process's STDOUT pipe was created!

SetHandleInformation() - pipe STDOUT read handle is not inherited!

CreatePipe() - pipe for child process's STDIN was created!

Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!

Verify: argv[1] = justatextfile.txt

Creating the child process...

Child process ID is: 1144

Child thread ID is: 4856

piProcInfo.hProcess handle was closed!

piProcInfo.hThread handle was closed!

CreateFile() - justatextfile.txt was successfully opened!

ReadFile() - Reading from a file...

WriteFile() - writing to the pipe for the child's STDIN...

ReadFile() - Failed to read file! Error 0

Closing the pipe handle...

Contents of justatextfile.txt written to child STDIN pipe.

Contents of justatextfile.txt child process STDOUT:

ReadFile() from child's standard output is OK!

Microsoft Windows XP [Version 5.1.2600]

WriteFile() to parent's standard output is OK!

ReadFile() from child's standard output is OK!

(C) Copyright 1985-2001 Microsoft Corp.

C:\amad\buffalo\Debug>

WriteFile() to parent's standard output is OK!

ReadFile() from child's standard output is OK!

Well, CreateProcess() looks OK.

 

 

WriteFile() to parent's standard output is OK!

ReadFile() from child's standard output failed! Error 109

End of parent execution.

C:\amad\buffalo\Debug>

 

Error 109 (ERROR_BROKEN_PIPE) means the pipe has been ended which is normal in this case.

 

 

C and C++ Programming Resources | C & C++ Code Example Index