/// <summary> /// Starts a child process as specified in <paramref name="startInfo"/>. /// </summary> /// <param name="startInfo"><see cref="ChildProcessStartInfo"/>.</param> /// <returns>The started process.</returns> /// <exception cref="IOException">Failed to open a specified file.</exception> /// <exception cref="ProcessCreationFailedException">Process creation failed.</exception> public static ChildProcess Start(ChildProcessStartInfo startInfo) { startInfo = startInfo ?? throw new ArgumentNullException(nameof(startInfo)); using (var stdHandles = new PipelineStdHandleCreator(startInfo)) { var processHandle = ProcessPal.SpawnProcess( fileName: startInfo.FileName, arguments: startInfo.Arguments, workingDirectory: startInfo.WorkingDirectory, environmentVariables: startInfo.EnvironmentVariables, stdIn: stdHandles.PipelineStdIn, stdOut: stdHandles.PipelineStdOut, stdErr: stdHandles.PipelineStdErr); try { var process = new ChildProcess(processHandle, stdHandles.InputStream, stdHandles.OutputStream, stdHandles.ErrorStream); stdHandles.DetachStreams(); return(process); } catch { processHandle.Dispose(); throw; } } }
public async Task PipesAreAsynchronous() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.InputPipe, StdOutputRedirection = OutputRedirection.OutputPipe, StdErrorRedirection = OutputRedirection.ErrorPipe, }; using (var sut = ChildProcess.Start(si)) { Assert.True(((FileStream)sut.StandardInput).IsAsync); Assert.True(((FileStream)sut.StandardOutput).IsAsync); Assert.True(((FileStream)sut.StandardError).IsAsync); using (var sr = new StreamReader(sut.StandardOutput)) { const string text = "foobar"; var stdoutTask = sr.ReadToEndAsync(); using (var sw = new StreamWriter(sut.StandardInput)) { await sw.WriteAsync(text); } Assert.Equal(text, await stdoutTask); } sut.WaitForExit(); Assert.Equal(0, sut.ExitCode); } }
public async Task CorrectlyConnectOutputPipes() { { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.OutputPipe, StdErrorRedirection = OutputRedirection.ErrorPipe, }; using (var sut = ChildProcess.Start(si)) { await ChildProcessAssert.CorrectlyConnectsPipesAsync(sut, "TestChild.Out", "TestChild.Error"); } } { // invert stdout and stderr var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.ErrorPipe, StdErrorRedirection = OutputRedirection.OutputPipe, }; using (var sut = ChildProcess.Start(si)) { await ChildProcessAssert.CorrectlyConnectsPipesAsync(sut, "TestChild.Error", "TestChild.Out"); } } }
public void RedirectionToHandle() { using (var tmp = new TemporaryDirectory()) { var inFile = Path.Combine(tmp.Location, "in"); var outFile = Path.Combine(tmp.Location, "out"); var errFile = Path.Combine(tmp.Location, "err"); // StdOutputHandle StdErrorHandle { using (var fsOut = File.Create(outFile)) using (var fsErr = File.Create(errFile)) { // File var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.Handle, StdOutputHandle = fsOut.SafeFileHandle, StdErrorRedirection = OutputRedirection.Handle, StdErrorHandle = fsErr.SafeFileHandle, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } } Assert.Equal("TestChild.Out", File.ReadAllText(outFile)); Assert.Equal("TestChild.Error", File.ReadAllText(errFile)); } // StdInputHandle { const string text = "foobar"; File.WriteAllText(inFile, text); using (var fsIn = File.OpenRead(inFile)) { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.Handle, StdInputHandle = fsIn.SafeFileHandle, StdOutputRedirection = OutputRedirection.File, StdOutputFile = outFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } } Assert.Equal(text, File.ReadAllText(outFile)); } } }
private static ChildProcess CreateForWaitForExitTest() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.InputPipe, StdOutputRedirection = OutputRedirection.NullDevice, }; return(ChildProcess.Start(si)); }
public void CanCreateChildProcess() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath) { StdOutputRedirection = OutputRedirection.OutputPipe, }; using (var sut = ChildProcess.Start(si)) { ChildProcessAssert.CanCreateChildProcess(sut); } }
public void ChildProcessWaitForAsyncIsTrulyAsynchronous() { var si = new ChildProcessStartInfo(TestUtil.TestChildPath, "Sleep", "1000") { StdOutputRedirection = OutputRedirection.NullDevice, StdErrorRedirection = OutputRedirection.NullDevice, }; using (var sut = ChildProcess.Start(si)) { WaitForAsyncIsTrulyAsynchronous(sut); } }
public void ChildProcessWaitForAsyncIsTrulyAsynchronous() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommandName, TestUtil.TestChildPath, "Sleep", "1000") { StdOutputRedirection = OutputRedirection.NullDevice, StdErrorRedirection = OutputRedirection.NullDevice, }; using var sut = ChildProcess.Start(si); WaitForAsyncIsTrulyAsynchronous(sut); sut.WaitForExit(); Assert.True(sut.IsSuccessful); }
public async Task PipesAreAsynchronous() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.InputPipe, StdOutputRedirection = OutputRedirection.OutputPipe, StdErrorRedirection = OutputRedirection.ErrorPipe, }; using (var sut = ChildProcess.Start(si)) { await ChildProcessAssert.PipesAreAsynchronousAsync(sut); } }
/// <summary> /// Starts a child process as specified in <paramref name="startInfo"/>. /// </summary> /// <param name="startInfo"><see cref="ChildProcessStartInfo"/>.</param> /// <returns>The started process.</returns> /// <exception cref="ArgumentException"><paramref name="startInfo"/> has an invalid value.</exception> /// <exception cref="ArgumentNullException"><paramref name="startInfo"/> is null.</exception> /// <exception cref="FileNotFoundException">The executable not found.</exception> /// <exception cref="IOException">Failed to open a specified file.</exception> /// <exception cref="AsmichiChildProcessLibraryCrashedException">The operation failed due to critical disturbance.</exception> /// <exception cref="Win32Exception">Another kind of native errors.</exception> public static IChildProcess Start(ChildProcessStartInfo startInfo) { _ = startInfo ?? throw new ArgumentNullException(nameof(startInfo)); var startInfoInternal = new ChildProcessStartInfoInternal(startInfo); _ = startInfoInternal.FileName ?? throw new ArgumentException("ChildProcessStartInfo.FileName must not be null.", nameof(startInfo)); _ = startInfoInternal.Arguments ?? throw new ArgumentException("ChildProcessStartInfo.Arguments must not be null.", nameof(startInfo)); var flags = startInfoInternal.Flags; if (flags.HasUseCustomCodePage() && !flags.HasCreateNewConsole()) { throw new ArgumentException( $"{nameof(ChildProcessFlags.UseCustomCodePage)} requires {nameof(ChildProcessFlags.CreateNewConsole)}.", nameof(startInfo)); } var resolvedPath = ResolveExecutablePath(startInfoInternal.FileName, startInfoInternal.Flags); using var stdHandles = new PipelineStdHandleCreator(ref startInfoInternal); IChildProcessStateHolder processState; try { processState = ChildProcessContext.Shared.SpawnProcess( startInfo: ref startInfoInternal, resolvedPath: resolvedPath, stdIn: stdHandles.PipelineStdIn, stdOut: stdHandles.PipelineStdOut, stdErr: stdHandles.PipelineStdErr); } catch (Win32Exception ex) { if (EnvironmentPal.IsFileNotFoundError(ex.NativeErrorCode)) { ThrowHelper.ThrowExecutableNotFoundException(resolvedPath, startInfoInternal.Flags, ex); } // Win32Exception does not provide detailed information by its type. // The NativeErrorCode and Message property should be enough because normally there is // nothing we can do to programmatically recover from this error. throw; } var process = new ChildProcessImpl(processState, stdHandles.InputStream, stdHandles.OutputStream, stdHandles.ErrorStream); stdHandles.DetachStreams(); return(process); }
public void CanCreateChildProcess() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommandName, TestUtil.TestChildPath) { StdOutputRedirection = OutputRedirection.OutputPipe, }; using var sut = ChildProcess.Start(si); sut.WaitForExit(); Assert.Equal(0, sut.ExitCode); // This closes StandardOutput, which should be acceptable. using var sr = new StreamReader(sut.StandardOutput !); Assert.Equal("TestChild", sr.ReadToEnd()); }
public void DefaultValueTest() { var sut = new ChildProcessStartInfo(); Assert.Equal(InputRedirection.NullDevice, sut.StdInputRedirection); Assert.Equal(OutputRedirection.ParentOutput, sut.StdOutputRedirection); Assert.Equal(OutputRedirection.ParentError, sut.StdErrorRedirection); Assert.Null(sut.StdInputFile); Assert.Null(sut.StdInputHandle); Assert.Null(sut.StdOutputFile); Assert.Null(sut.StdOutputHandle); Assert.Null(sut.StdErrorFile); Assert.Null(sut.StdErrorHandle); Assert.Null(sut.FileName); Assert.Null(sut.Arguments); Assert.Null(sut.WorkingDirectory); Assert.Null(sut.EnvironmentVariables); }
public void CanRedirectToSameFile() { using (var tmp = new TemporaryDirectory()) { var outFile = Path.Combine(tmp.Location, "out"); // StdOutputFile StdErrorFile { // File var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.File, StdOutputFile = outFile, StdErrorRedirection = OutputRedirection.File, StdErrorFile = outFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } Assert.Equal("TestChild.OutTestChild.Error", File.ReadAllText(outFile)); // AppendToFile si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.AppendToFile, StdOutputFile = outFile, StdErrorRedirection = OutputRedirection.AppendToFile, StdErrorFile = outFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } Assert.Equal("TestChild.OutTestChild.ErrorTestChild.OutTestChild.Error", File.ReadAllText(outFile)); } } }
public void ExitCodeThrowsBeforeChildExits() { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.InputPipe, }; using (var sut = ChildProcess.Start(si)) { Assert.Throws <InvalidOperationException>(() => sut.IsSuccessful); Assert.Throws <InvalidOperationException>(() => sut.ExitCode); sut.StandardInput.Close(); sut.WaitForExit(); Assert.True(sut.IsSuccessful); Assert.Equal(0, sut.ExitCode); } }
public void DefaultValueTest() { var sut = new ChildProcessStartInfo(); Assert.Equal(InputRedirection.NullDevice, sut.StdInputRedirection); Assert.Equal(OutputRedirection.ParentOutput, sut.StdOutputRedirection); Assert.Equal(OutputRedirection.ParentError, sut.StdErrorRedirection); Assert.Null(sut.StdInputFile); Assert.Null(sut.StdInputHandle); Assert.Null(sut.StdOutputFile); Assert.Null(sut.StdOutputHandle); Assert.Null(sut.StdErrorFile); Assert.Null(sut.StdErrorHandle); Assert.Null(sut.FileName); Assert.Equal(sut.Arguments, Array.Empty <string>()); Assert.Null(sut.WorkingDirectory); Assert.Null(sut.EnvironmentVariables); Assert.Equal(ChildProcessFlags.None, sut.Flags); Assert.Equal(65001, sut.CodePage); }
public ChildProcessStartInfoInternal(ChildProcessStartInfo startInfo) { FileName = startInfo.FileName; Arguments = startInfo.Arguments; WorkingDirectory = startInfo.WorkingDirectory; EnvironmentVariables = startInfo.EnvironmentVariables; Flags = startInfo.Flags; CodePage = startInfo.CodePage; SearchPath = startInfo.SearchPath; StdInputRedirection = startInfo.StdInputRedirection; StdOutputRedirection = startInfo.StdOutputRedirection; StdErrorRedirection = startInfo.StdErrorRedirection; StdInputFile = startInfo.StdInputFile; StdOutputFile = startInfo.StdOutputFile; StdErrorFile = startInfo.StdErrorFile; StdInputHandle = startInfo.StdInputHandle; StdOutputHandle = startInfo.StdOutputHandle; StdErrorHandle = startInfo.StdErrorHandle; // Additional parameters CreateNewConsole = Flags.HasCreateNewConsole() || !ConsolePal.HasConsoleWindow(); }
public void CanObtainExitCode() { { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "ExitCode", "0"); using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); Assert.Equal(0, sut.ExitCode); } } { var si = new ChildProcessStartInfo(TestUtil.DotnetCommand, TestUtil.TestChildPath, "ExitCode", "-1"); using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.False(sut.IsSuccessful); Assert.Equal(-1, sut.ExitCode); } } }
public void RedirectionToFile() { using (var tmp = new TemporaryDirectory()) { var inFile = Path.Combine(tmp.Location, "in"); var outFile = Path.Combine(tmp.Location, "out"); var errFile = Path.Combine(tmp.Location, "err"); // StdOutputFile StdErrorFile { // File var si = new ChildProcessStartInfo(TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.File, StdOutputFile = outFile, StdErrorRedirection = OutputRedirection.File, StdErrorFile = errFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } Assert.Equal("TestChild.Out", File.ReadAllText(outFile)); Assert.Equal("TestChild.Error", File.ReadAllText(errFile)); // AppendToFile si = new ChildProcessStartInfo(TestUtil.TestChildPath, "EchoOutAndError") { StdOutputRedirection = OutputRedirection.AppendToFile, StdOutputFile = errFile, StdErrorRedirection = OutputRedirection.AppendToFile, StdErrorFile = outFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } Assert.Equal("TestChild.OutTestChild.Error", File.ReadAllText(outFile)); Assert.Equal("TestChild.ErrorTestChild.Out", File.ReadAllText(errFile)); } // StdInputFile { const string text = "foobar"; File.WriteAllText(inFile, text); var si = new ChildProcessStartInfo(TestUtil.TestChildPath, "EchoBack") { StdInputRedirection = InputRedirection.File, StdInputFile = inFile, StdOutputRedirection = OutputRedirection.File, StdOutputFile = outFile, }; using (var sut = ChildProcess.Start(si)) { sut.WaitForExit(); Assert.True(sut.IsSuccessful); } Assert.Equal(text, File.ReadAllText(outFile)); } } }
public void ReportsCreationFailure() { var si = new ChildProcessStartInfo("nonexistentfile"); Assert.Throws <ProcessCreationFailedException>(() => ChildProcess.Start(si)); }
public PipelineStdHandleCreator(ChildProcessStartInfo startInfo) { var stdInputRedirection = startInfo.StdInputRedirection; var stdOutputRedirection = startInfo.StdOutputRedirection; var stdErrorRedirection = startInfo.StdErrorRedirection; var stdInputFile = startInfo.StdInputFile; var stdOutputFile = startInfo.StdOutputFile; var stdErrorFile = startInfo.StdErrorFile; var stdInputHandle = startInfo.StdInputHandle; var stdOutputHandle = startInfo.StdOutputHandle; var stdErrorHandle = startInfo.StdErrorHandle; if (stdInputRedirection == InputRedirection.Handle && stdInputHandle == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdInputHandle must not be null."); } if (stdInputRedirection == InputRedirection.File && stdInputFile == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdInputFile must not be null."); } if (stdOutputRedirection == OutputRedirection.Handle && stdOutputHandle == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdOutputHandle must not be null."); } if (IsFileRedirection(stdOutputRedirection) && stdOutputFile == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdOutputFile must not be null."); } if (stdErrorRedirection == OutputRedirection.Handle && stdErrorHandle == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdErrorHandle must not be null."); } if (IsFileRedirection(stdErrorRedirection) && stdErrorFile == null) { throw new ArgumentNullException(nameof(startInfo), "ChildProcessStartInfo.StdErrorFile must not be null."); } bool redirectingToSameFile = IsFileRedirection(stdOutputRedirection) && IsFileRedirection(stdErrorRedirection) && stdOutputFile == stdErrorFile; if (redirectingToSameFile && stdErrorRedirection != stdOutputRedirection) { throw new ArgumentException( "StdOutputRedirection and StdErrorRedirection must be the same value when both stdout and stderr redirect to the same file.", nameof(startInfo)); } try { if (stdInputRedirection == InputRedirection.InputPipe) { (this.InputStream, _inputReadPipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.Out); } if (stdOutputRedirection == OutputRedirection.OutputPipe || stdErrorRedirection == OutputRedirection.OutputPipe) { (this.OutputStream, _outputWritePipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.In); } if (stdOutputRedirection == OutputRedirection.ErrorPipe || stdErrorRedirection == OutputRedirection.ErrorPipe) { (this.ErrorStream, _errorWritePipe) = FilePal.CreatePipePairWithAsyncServerSide(System.IO.Pipes.PipeDirection.In); } this.PipelineStdIn = ChooseInput( stdInputRedirection, stdInputFile, stdInputHandle, _inputReadPipe); this.PipelineStdOut = ChooseOutput( stdOutputRedirection, stdOutputFile, stdOutputHandle, _outputWritePipe, _errorWritePipe); if (redirectingToSameFile) { this.PipelineStdErr = this.PipelineStdOut; } else { this.PipelineStdErr = ChooseOutput( stdErrorRedirection, stdErrorFile, stdErrorHandle, _outputWritePipe, _errorWritePipe); } } catch { Dispose(); throw; } }