RESOLUTION
To work around this issue, wrap the child-creation code in a critical section. This prevents any accidental inheritance. For this method to work properly, create the pipes as noninheritable by setting the security descriptor to NULL. Then, set the ends of the pipe that you want the child to inherit as inheritable by using the
SetHandleInformation function call, as demonstrated in the following sample code:
CRITICAL_SECTION cs;
HANDLE hReadIn, hWriteIn;
HANDLE hReadOut, hWriteOut;
HANDLE hReadErr, hWriteErr;
InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);
if ( !CreatePipe(&hReadIn, &hWriteIn, NULL, 0) )
{
// an error occurred
}
if ( !CreatePipe(&hReadOut, &hWriteOut, NULL, 0) )
{
// an error occurred
}
if ( !CreatePipe(&hReadErr, &hWriteErr, NULL, 0) )
{
// an error occurred
}
if ( !SetHandleInformation(hReadIn, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
// an error occurred
}
if ( !SetHandleInformation(hWriteOut, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
// an error occurred
}
if ( !SetHandleInformation(hWriteErr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
// an error occurred
}
STARTUP_INFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hReadIn;
si.hStdOutput = hWriteOut;
si.hStdError = hWriteErr;
if ( !CreateProcess( "child.exe",
NULL,
NULL, NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
lpEnvironment,
&si, &pi) )
{
// an error occurred
}
CloseHandle(hReadIn);
CloseHandle(hWriteOut);
CloseHandle(hWriteErr);
LeaveCriticalSection(&cs);
Note that the preceding solution does not come without costs. The creation of critical sections in code is sometimes messy and comes at a price of degraded performance. There is another workaround to this issue, and it requires the creation of an intermediate application to launch the child. However, this solution also has its downside. The main disadvantage to this method is that the parent basically loses the ease with which it obtains the child's process ID. If the parent needs the process ID, the intermediate process must pass it back somehow.
By using this intermediate application method, you avoid any accidental inheritance by relying on Windows to do the work for you. Any accidentally inherited handles in the intermediate process will definitely not be duplicated across to the child. This is guaranteed if you specify FALSE for the
bInheritHandles parameter in the call to
CreateProcess in the intermediate application. Your pipe handles will still be duplicated because Windows will always duplicate the STD handles, even when
bInheritHandles is set to FALSE.
Parent Application
HANDLE hReadIn, hWriteIn;
HANDLE hReadOut, hWriteOut;
HANDLE hReadErr, hWriteErr;
if ( !CreatePipe(&hReadIn, &hWriteIn, NULL, 0) )
{
// an error occurred
}
if ( !CreatePipe(&hReadOut, &hWriteOut, NULL, 0) )
{
// an error occurred
}
if ( !CreatePipe(&hReadErr, &hWriteErr, NULL, 0) )
{
// an error occurred
}
STARTUP_INFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hReadIn;
si.hStdOutput = hWriteOut;
si.hStdError = hWriteErr;
if ( !CreateProcess( "Intermediate.exe",
NULL,
NULL, NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
lpEnvironment,
&si, &pi) )
{
// an error occurred
}
CloseHandle(hReadIn);
CloseHandle(hWriteOut);
CloseHandle(hWriteErr);
Intermediate Application
STARTUP_INFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
if ( !CreateProcess( "child.exe",
NULL,
NULL, NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
lpEnvironment,
&si, &pi) )
{
// an error occurred
}