/// <summary> /// Constructor /// </summary> /// <param name="terminalParameter">Terminal parameter</param> /// <param name="socket">PipeSocket object</param> /// <param name="pipedProcess">Process data (or null)</param> public PipeTerminalConnection(PipeTerminalParameter terminalParameter, PipeSocket socket, PipedProcess pipedProcess) { _terminalOutput = new PipeTerminalOutput(); _terminalParameter = terminalParameter; _socket = socket; _pipedProcess = pipedProcess; if (_pipedProcess != null) { _pipedProcess.Exited += delegate(object sender, EventArgs e) { _socket.ProcessExited(); }; } }
/// <summary> /// Start exe file and create a new PipeTerminalConnection /// </summary> /// <param name="param">Terminal parameter</param> /// <returns>created object</returns> /// <exception cref="Win32Exception">Error in Win32 API</exception> private static PipeTerminalConnection OpenExeFile(PipeTerminalParameter param) { // System.Diagnostics.Process has functionality that creates STDIN/STDOUT/STDERR pipe. // But we need two pipes. One connects to STDIN and another one connects to STDOUT and STDERR. // So we use Win32 API to invoke a new process. SafeFileHandle parentReadHandle = null; SafeFileHandle parentWriteHandle = null; SafeFileHandle childReadHandle = null; SafeFileHandle childWriteHandle = null; SafeFileHandle childStdInHandle = null; SafeFileHandle childStdOutHandle = null; SafeFileHandle childStdErrHandle = null; FileStream parentReadStream = null; FileStream parentWriteStream = null; try { // Create pipes CreateAsyncPipe(out parentReadHandle, true, out childWriteHandle, false); CreateAsyncPipe(out childReadHandle, false, out parentWriteHandle, true); // Duplicate handles as inheritable handles. childStdOutHandle = DuplicatePipeHandle(childWriteHandle, true, "ChildWrite"); childStdInHandle = DuplicatePipeHandle(childReadHandle, true, "ChildRead"); childStdErrHandle = DuplicatePipeHandle(childWriteHandle, true, "ChildWrite"); // Close non-inheritable handles childWriteHandle.Dispose(); childWriteHandle = null; childReadHandle.Dispose(); childReadHandle = null; // Create parent side streams parentReadStream = new FileStream(parentReadHandle, FileAccess.Read, 4096, true /*Async*/); parentWriteStream = new FileStream(parentWriteHandle, FileAccess.Write, 4096, true /*Async*/); // Prepare command line string commandLine = GetCommandLine(param.ExeFilePath, param.CommandLineOptions); // Determine character encoding of the environment variables bool unicodeEnvironment = (Environment.OSVersion.Platform == PlatformID.Win32NT) ? true : false; // Prepare flags // Note: // We use CREATE_NEW_CONSOLE for separating console. // It disables CREATE_NO_WINDOW, so we use setting below // to hide the console window. // STARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW // STARTUPINFO.wShowWindow = SW_HIDE int creationFlags = CREATE_NEW_CONSOLE /*| CREATE_NO_WINDOW*/; if (unicodeEnvironment) { creationFlags |= CREATE_UNICODE_ENVIRONMENT; } // Prepare environment variables Dictionary <String, String> envDict = new Dictionary <String, String>(); foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { string key = entry.Key as string; string value = entry.Value as string; if (key != null && value != null) { envDict.Add(key.ToLowerInvariant(), key + "=" + value); } } if (param.EnvironmentVariables != null) { foreach (PipeTerminalParameter.EnvironmentVariable ev in param.EnvironmentVariables) { string expandedValue = Environment.ExpandEnvironmentVariables(ev.Value); string key = ev.Name.ToLowerInvariant(); envDict.Remove(key); envDict.Add(key, ev.Name + "=" + expandedValue); } } byte[] environmentByteArray = GetEnvironmentBytes(envDict, unicodeEnvironment); // Prepare current directory string currentDirectory = Path.GetDirectoryName(param.ExeFilePath); // Prepare STARTUPINFO STARTUPINFO startupInfo = new STARTUPINFO(); startupInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startupInfo.hStdInput = childStdInHandle; startupInfo.hStdOutput = childStdOutHandle; startupInfo.hStdError = childStdErrHandle; startupInfo.wShowWindow = SW_HIDE; // Prepare PROCESS_INFORMATION PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION(); // Start process GCHandle environmentGCHandle = GCHandle.Alloc(environmentByteArray, GCHandleType.Pinned); bool apiret = CreateProcess( null, commandLine, IntPtr.Zero, IntPtr.Zero, true, creationFlags, environmentGCHandle.AddrOfPinnedObject(), currentDirectory, startupInfo, ref processInfo ); environmentGCHandle.Free(); if (!apiret) { throw new Win32Exception("CreateProcess", Marshal.GetLastWin32Error(), "commandLine=" + commandLine); } Process process = Process.GetProcessById(processInfo.dwProcessId); PipedProcess pipedProcess = new PipedProcess(process, childStdInHandle, childStdOutHandle, childStdErrHandle); PipeSocket socket = new PipeSocket(parentReadStream, parentWriteStream); PipeTerminalConnection connection = new PipeTerminalConnection(param, socket, pipedProcess); return(connection); } catch (Exception) { if (parentReadStream != null) { parentReadStream.Dispose(); } if (parentWriteStream != null) { parentWriteStream.Dispose(); } if (parentReadHandle != null) { parentReadHandle.Dispose(); } if (parentWriteHandle != null) { parentWriteHandle.Dispose(); } if (childReadHandle != null) { childReadHandle.Dispose(); } if (childWriteHandle != null) { childWriteHandle.Dispose(); } if (childStdInHandle != null) { childStdInHandle.Dispose(); } if (childStdOutHandle != null) { childStdOutHandle.Dispose(); } if (childStdErrHandle != null) { childStdErrHandle.Dispose(); } throw; } }
/// <summary> /// Start exe file and create a new PipeTerminalConnection /// </summary> /// <param name="param">Terminal parameter</param> /// <returns>created object</returns> /// <exception cref="Win32Exception">Error in Win32 API</exception> private static PipeTerminalConnection OpenExeFile(PipeTerminalParameter param) { // System.Diagnostics.Process has functionality that creates STDIN/STDOUT/STDERR pipe. // But we need two pipes. One connects to STDIN and another one connects to STDOUT and STDERR. // So we use Win32 API to invoke a new process. SafeFileHandle parentReadHandle = null; SafeFileHandle parentWriteHandle = null; SafeFileHandle childReadHandle = null; SafeFileHandle childWriteHandle = null; SafeFileHandle childStdInHandle = null; SafeFileHandle childStdOutHandle = null; SafeFileHandle childStdErrHandle = null; FileStream parentReadStream = null; FileStream parentWriteStream = null; try { // Create pipes CreateAsyncPipe(out parentReadHandle, true, out childWriteHandle, false); CreateAsyncPipe(out childReadHandle, false, out parentWriteHandle, true); // Duplicate handles as inheritable handles. childStdOutHandle = DuplicatePipeHandle(childWriteHandle, true, "ChildWrite"); childStdInHandle = DuplicatePipeHandle(childReadHandle, true, "ChildRead"); childStdErrHandle = DuplicatePipeHandle(childWriteHandle, true, "ChildWrite"); // Close non-inheritable handles childWriteHandle.Dispose(); childWriteHandle = null; childReadHandle.Dispose(); childReadHandle = null; // Create parent side streams parentReadStream = new FileStream(parentReadHandle, FileAccess.Read, 4096, true /*Async*/); parentWriteStream = new FileStream(parentWriteHandle, FileAccess.Write, 4096, true /*Async*/); // Prepare command line string commandLine = GetCommandLine(param.ExeFilePath, param.CommandLineOptions); // Determine character encoding of the environment variables bool unicodeEnvironment = (Environment.OSVersion.Platform == PlatformID.Win32NT) ? true : false; // Prepare flags // Note: // We use CREATE_NEW_CONSOLE for separating console. // It disables CREATE_NO_WINDOW, so we use setting below // to hide the console window. // STARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW // STARTUPINFO.wShowWindow = SW_HIDE int creationFlags = CREATE_NEW_CONSOLE /*| CREATE_NO_WINDOW*/; if (unicodeEnvironment) creationFlags |= CREATE_UNICODE_ENVIRONMENT; // Prepare environment variables Dictionary<String, String> envDict = new Dictionary<String, String>(); foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) { string key = entry.Key as string; string value = entry.Value as string; if (key != null && value != null) { envDict.Add(key.ToLowerInvariant(), key + "=" + value); } } if (param.EnvironmentVariables != null) { foreach (PipeTerminalParameter.EnvironmentVariable ev in param.EnvironmentVariables) { string expandedValue = Environment.ExpandEnvironmentVariables(ev.Value); string key = ev.Name.ToLowerInvariant(); envDict.Remove(key); envDict.Add(key, ev.Name + "=" + expandedValue); } } byte[] environmentByteArray = GetEnvironmentBytes(envDict, unicodeEnvironment); // Prepare current directory string currentDirectory = Path.GetDirectoryName(param.ExeFilePath); // Prepare STARTUPINFO STARTUPINFO startupInfo = new STARTUPINFO(); startupInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startupInfo.hStdInput = childStdInHandle; startupInfo.hStdOutput = childStdOutHandle; startupInfo.hStdError = childStdErrHandle; startupInfo.wShowWindow = SW_HIDE; // Prepare PROCESS_INFORMATION PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION(); // Start process GCHandle environmentGCHandle = GCHandle.Alloc(environmentByteArray, GCHandleType.Pinned); bool apiret = CreateProcess( null, commandLine, IntPtr.Zero, IntPtr.Zero, true, creationFlags, environmentGCHandle.AddrOfPinnedObject(), currentDirectory, startupInfo, ref processInfo ); environmentGCHandle.Free(); if (!apiret) throw new Win32Exception("CreateProcess", Marshal.GetLastWin32Error(), "commandLine=" + commandLine); Process process = Process.GetProcessById(processInfo.dwProcessId); PipedProcess pipedProcess = new PipedProcess(process, childStdInHandle, childStdOutHandle, childStdErrHandle); PipeSocket socket = new PipeSocket(parentReadStream, parentWriteStream); PipeTerminalConnection connection = new PipeTerminalConnection(param, socket, pipedProcess); return connection; } catch (Exception) { if (parentReadStream != null) parentReadStream.Dispose(); if (parentWriteStream != null) parentWriteStream.Dispose(); if (parentReadHandle != null) parentReadHandle.Dispose(); if (parentWriteHandle != null) parentWriteHandle.Dispose(); if (childReadHandle != null) childReadHandle.Dispose(); if (childWriteHandle != null) childWriteHandle.Dispose(); if (childStdInHandle != null) childStdInHandle.Dispose(); if (childStdOutHandle != null) childStdOutHandle.Dispose(); if (childStdErrHandle != null) childStdErrHandle.Dispose(); throw; } }