private bool StartCore(ProcessStartInfo startInfo)
{
// See knowledge base article Q190351 for an explanation of the following code. Noteworthy tricky points:
// * The handles are duplicated as non-inheritable before they are passed to CreateProcess so
// that the child process can not close them
// * CreateProcess allows you to redirect all or none of the standard IO handles, so we use
// GetStdHandle for the handles that are not being redirected
StringBuilder commandLine = BuildCommandLine(startInfo.FileName, startInfo.Arguments);
Interop.Kernel32.STARTUPINFO startupInfo = new Interop.Kernel32.STARTUPINFO();
Interop.Kernel32.PROCESS_INFORMATION processInfo = new Interop.Kernel32.PROCESS_INFORMATION();
Interop.Kernel32.SECURITY_ATTRIBUTES unused_SecAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES();
SafeProcessHandle procSH = new SafeProcessHandle();
SafeThreadHandle threadSH = new SafeThreadHandle();
bool retVal;
int errorCode = 0;
// handles used in parent process
SafeFileHandle standardInputWritePipeHandle = null;
SafeFileHandle standardOutputReadPipeHandle = null;
SafeFileHandle standardErrorReadPipeHandle = null;
GCHandle environmentHandle = new GCHandle();
lock (s_createProcessLock)
{
try
{
// set up the streams
if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
{
if (startInfo.RedirectStandardInput)
{
CreatePipe(out standardInputWritePipeHandle, out startupInfo.hStdInput, true);
}
else
{
startupInfo.hStdInput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE), false);
}
if (startInfo.RedirectStandardOutput)
{
CreatePipe(out standardOutputReadPipeHandle, out startupInfo.hStdOutput, false);
}
else
{
startupInfo.hStdOutput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
}
if (startInfo.RedirectStandardError)
{
CreatePipe(out standardErrorReadPipeHandle, out startupInfo.hStdError, false);
}
else
{
startupInfo.hStdError = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_ERROR_HANDLE), false);
}
startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
}
// set up the creation flags parameter
int creationFlags = 0;
if (startInfo.CreateNoWindow) creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_NO_WINDOW;
// set up the environment block parameter
IntPtr environmentPtr = (IntPtr)0;
if (startInfo._environmentVariables != null)
{
creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_UNICODE_ENVIRONMENT;
byte[] environmentBytes = EnvironmentVariablesToByteArray(startInfo._environmentVariables);
environmentHandle = GCHandle.Alloc(environmentBytes, GCHandleType.Pinned);
environmentPtr = environmentHandle.AddrOfPinnedObject();
}
string workingDirectory = startInfo.WorkingDirectory;
if (workingDirectory == string.Empty)
workingDirectory = Directory.GetCurrentDirectory();
if (startInfo.UserName.Length != 0)
{
if (startInfo.Password != null && startInfo.PasswordInClearText != null)
{
throw new ArgumentException(SR.CantSetDuplicatePassword);
}
Interop.Advapi32.LogonFlags logonFlags = (Interop.Advapi32.LogonFlags)0;
if (startInfo.LoadUserProfile)
{
logonFlags = Interop.Advapi32.LogonFlags.LOGON_WITH_PROFILE;
}
if (startInfo.Password != null)
{
IntPtr passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(startInfo.Password);
try
{
retVal = Interop.Advapi32.CreateProcessWithLogonW(
startInfo.UserName,
startInfo.Domain,
passwordPtr,
logonFlags,
null, // we don't need this since all the info is in commandLine
commandLine,
creationFlags,
environmentPtr,
workingDirectory,
startupInfo, // pointer to STARTUPINFO
processInfo // pointer to PROCESS_INFORMATION
);
if (!retVal)
errorCode = Marshal.GetLastWin32Error();
}
finally { Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr); }
}
else
{
unsafe
{
fixed (char* passwordPtr = startInfo.PasswordInClearText ?? string.Empty)
{
retVal = Interop.Advapi32.CreateProcessWithLogonW(
startInfo.UserName,
startInfo.Domain,
(IntPtr)passwordPtr,
logonFlags,
null, // we don't need this since all the info is in commandLine
commandLine,
creationFlags,
environmentPtr,
workingDirectory,
startupInfo, // pointer to STARTUPINFO
processInfo // pointer to PROCESS_INFORMATION
);
}
}
if (!retVal)
errorCode = Marshal.GetLastWin32Error();
}
if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
procSH.InitialSetHandle(processInfo.hProcess);
if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
threadSH.InitialSetHandle(processInfo.hThread);
if (!retVal)
{
if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
{
throw new Win32Exception(errorCode, SR.InvalidApplication);
}
throw new Win32Exception(errorCode);
}
}
else
{
retVal = Interop.Kernel32.CreateProcess(
null, // we don't need this since all the info is in commandLine
commandLine, // pointer to the command line string
ref unused_SecAttrs, // address to process security attributes, we don't need to inherit the handle
ref unused_SecAttrs, // address to thread security attributes.
true, // handle inheritance flag
creationFlags, // creation flags
environmentPtr, // pointer to new environment block
workingDirectory, // pointer to current directory name
startupInfo, // pointer to STARTUPINFO
processInfo // pointer to PROCESS_INFORMATION
);
if (!retVal)
errorCode = Marshal.GetLastWin32Error();
if (processInfo.hProcess != (IntPtr)0 && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
procSH.InitialSetHandle(processInfo.hProcess);
if (processInfo.hThread != (IntPtr)0 && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
threadSH.InitialSetHandle(processInfo.hThread);
if (!retVal)
{
if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
{
throw new Win32Exception(errorCode, SR.InvalidApplication);
}
throw new Win32Exception(errorCode);
}
}
}
finally
{
// free environment block
if (environmentHandle.IsAllocated)
{
environmentHandle.Free();
}
startupInfo.Dispose();
}
}
if (startInfo.RedirectStandardInput)
{
Encoding enc = GetEncoding((int)Interop.Kernel32.GetConsoleCP());
_standardInput = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), enc, 4096);
_standardInput.AutoFlush = true;
}
if (startInfo.RedirectStandardOutput)
{
Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
_standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
}
if (startInfo.RedirectStandardError)
{
Encoding enc = startInfo.StandardErrorEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
_standardError = new StreamReader(new FileStream(standardErrorReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
}
bool ret = false;
if (!procSH.IsInvalid)
{
SetProcessHandle(procSH);
SetProcessId((int)processInfo.dwProcessId);
threadSH.Dispose();
ret = true;
}
return ret;
}