/// <summary> /// Executes target process with given input, waits until completion asynchronously and returns produced output. /// </summary> /// <param name="input">Execution input.</param> /// <param name="cancellationToken">Token that can be used to abort execution.</param> /// <param name="bufferHandler">Handler for real-time standard output and standard error data.</param> /// <remarks>The underlying process is killed if the execution is canceled.</remarks> public async Task <ExecutionOutput> ExecuteAsync(ExecutionInput input, CancellationToken cancellationToken = default(CancellationToken), IBufferHandler bufferHandler = null) { input.GuardNotNull(nameof(input)); // Create task completion sources var processTcs = new TaskCompletionSource <object>(); var stdOutTcs = new TaskCompletionSource <object>(); var stdErrTcs = new TaskCompletionSource <object>(); // Set up execution context using (var process = CreateProcess(input)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _killSwitchCts.Token)) { // Get linked cancellation token var linkedToken = linkedCts.Token; // Create buffers var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); // Wire events process.Exited += (sender, args) => processTcs.SetResult(null); process.OutputDataReceived += (sender, args) => { if (args.Data != null) { stdOutBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardOutput(args.Data); } else { stdOutTcs.SetResult(null); } }; process.ErrorDataReceived += (sender, args) => { if (args.Data != null) { stdErrBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardError(args.Data); } else { stdErrTcs.SetResult(null); } }; // Start process process.Start(); var startTime = DateTimeOffset.Now; // Begin reading stdout and stderr process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Write stdin using (process.StandardInput) await process.StandardInput.WriteAsync(input.StandardInput).ConfigureAwait(false); // Setup cancellation token // This has to be after process start so that it can actually be killed // and also after standard input so that it can write correctly linkedToken.Register(() => { // Kill process if it's not dead already process.KillIfRunning(); // Cancel tasks processTcs.SetCanceled(); stdOutTcs.SetCanceled(); stdErrTcs.SetCanceled(); }); // Wait until exit await processTcs.Task.ConfigureAwait(false); var exitTime = DateTimeOffset.Now; // Wait until stdout and stderr finished reading await stdOutTcs.Task.ConfigureAwait(false); await stdErrTcs.Task.ConfigureAwait(false); // Get stdout and stderr var stdOut = stdOutBuffer.ToString(); var stdErr = stdErrBuffer.ToString(); return(new ExecutionOutput(process.ExitCode, stdOut, stdErr, startTime, exitTime)); } }
/// <inheritdoc /> public ExecutionOutput Execute(ExecutionInput input, CancellationToken cancellationToken = default(CancellationToken), IBufferHandler bufferHandler = null) { input.GuardNotNull(nameof(input)); // Check if disposed ThrowIfDisposed(); // Set up execution context using (var processMre = new ManualResetEventSlim()) using (var stdOutMre = new ManualResetEventSlim()) using (var stdErrMre = new ManualResetEventSlim()) using (var linkedCts = LinkCancellationToken(cancellationToken)) using (var process = CreateProcess(input)) { // Get linked cancellation token var linkedToken = linkedCts.Token; // Create buffers var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); // Wire events process.Exited += (sender, args) => processMre.Set(); process.OutputDataReceived += (sender, args) => { if (args.Data != null) { stdOutBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardOutput(args.Data); } else { stdOutMre.Set(); } }; process.ErrorDataReceived += (sender, args) => { if (args.Data != null) { stdErrBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardError(args.Data); } else { stdErrMre.Set(); } }; // Start process process.Start(); var startTime = DateTimeOffset.Now; // Begin reading stdout and stderr process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Write stdin using (process.StandardInput) { if (input.StandardInput != null) { var stdinData = Settings.Encoding.StandardInput.GetBytes(input.StandardInput); var stdinStream = process.StandardInput.BaseStream; stdinStream.Write(stdinData, 0, stdinData.Length); } } // Setup cancellation token to kill process and set events // This has to be after process start so that it can actually be killed // and also after standard input so that it can write correctly linkedToken.Register(() => { process.TryKill(); processMre.Set(); stdOutMre.Set(); stdErrMre.Set(); }); // Cancellation token is not passed to waits because // the callback has to finish executing before the process is disposed // which otherwise would happen too soon // Wait until exit processMre.Wait(); var exitTime = DateTimeOffset.Now; // Wait until stdout and stderr finished reading stdOutMre.Wait(); stdErrMre.Wait(); // Check cancellation linkedToken.ThrowIfCancellationRequested(); // Get stdout and stderr var stdOut = stdOutBuffer.ToString(); var stdErr = stdErrBuffer.ToString(); return(new ExecutionOutput(process.ExitCode, stdOut, stdErr, startTime, exitTime)); } }
/// <summary> /// Executes target process with given input, waits until completion synchronously and returns produced output. /// </summary> /// <param name="input">Execution input.</param> /// <param name="cancellationToken">Token that can be used to abort execution.</param> /// <param name="bufferHandler">Handler for real-time standard output and standard error data.</param> /// <remarks>The underlying process is killed if the execution is canceled.</remarks> public ExecutionOutput Execute(ExecutionInput input, CancellationToken cancellationToken = default(CancellationToken), IBufferHandler bufferHandler = null) { input.GuardNotNull(nameof(input)); // Set up execution context using (var stdOutMre = new ManualResetEventSlim()) using (var stdErrMre = new ManualResetEventSlim()) using (var process = CreateProcess(input)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _killSwitchCts.Token)) { // Get linked cancellation token var linkedToken = linkedCts.Token; // Create buffers var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); // Wire events process.OutputDataReceived += (sender, args) => { if (args.Data != null) { stdOutBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardOutput(args.Data); } else { stdOutMre.Set(); } }; process.ErrorDataReceived += (sender, args) => { if (args.Data != null) { stdErrBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardError(args.Data); } else { stdErrMre.Set(); } }; // Start process process.Start(); var startTime = DateTimeOffset.Now; // Begin reading stdout and stderr process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Write stdin using (process.StandardInput) process.StandardInput.Write(input.StandardInput); // Setup cancellation token // This has to be after process start so that it can actually be killed // and also after standard input so that it can write correctly linkedToken.Register(() => { // Kill process if it's not dead already process.KillIfRunning(); }); // Wait until exit process.WaitForExit(); var exitTime = DateTimeOffset.Now; // Check cancellation linkedToken.ThrowIfCancellationRequested(); // Wait until stdout and stderr finished reading stdOutMre.Wait(linkedToken); stdErrMre.Wait(linkedToken); // Get stdout and stderr var stdOut = stdOutBuffer.ToString(); var stdErr = stdErrBuffer.ToString(); return(new ExecutionOutput(process.ExitCode, stdOut, stdErr, startTime, exitTime)); } }
public async Task <ExecutionOutput> Execute1Async(ExecutionInput input, CancellationToken cancellationToken = default(CancellationToken), IBufferHandler bufferHandler = null) { input.GuardNotNull(nameof(input)); // Check if disposed ThrowIfDisposed(); // Create task completion sources var processTcs = new TaskCompletionSource <object>(); var stdOutTcs = new TaskCompletionSource <object>(); var stdErrTcs = new TaskCompletionSource <object>(); // Set up execution context using (var process = CreateProcess1(input)) { // Get linked cancellation token var linkedToken = cancellationToken; // Create buffers var stdOutBuffer = new StringBuilder(); var stdErrBuffer = new StringBuilder(); // Wire events process.Exited += (sender, args) => processTcs.TrySetResult(null); process.OutputDataReceived += (sender, args) => { if (args.Data != null) { stdOutBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardOutput(args.Data); } else { stdOutTcs.TrySetResult(null); } }; process.ErrorDataReceived += (sender, args) => { if (args.Data != null) { stdErrBuffer.AppendLine(args.Data); bufferHandler?.HandleStandardError(args.Data); } else { stdErrTcs.TrySetResult(null); } }; // Start process process.Start(); var startTime = DateTimeOffset.Now; // Begin reading stdout and stderr process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Write stdin using (process.StandardInput) { var sr = new StreamWriter(process.StandardInput.BaseStream, Encoding.GetEncoding("GBK")); //sr.WriteLine("chcp 65001"); sr.WriteLine(input.Arguments); sr.Flush(); } // Setup cancellation token to kill process and cancel tasks // This has to be after process start so that it can actually be killed // and also after standard input so that it can write correctly linkedToken.Register(() => { CloseCmdProcess.CloseProcess(process); process.TryKill(); processTcs.TrySetCanceled(); stdOutTcs.TrySetCanceled(); stdErrTcs.TrySetCanceled(); }); // Wait until exit await processTcs.Task.ConfigureAwait(false); var exitTime = DateTimeOffset.Now; // Wait until stdout and stderr finished reading await stdOutTcs.Task.ConfigureAwait(false); await stdErrTcs.Task.ConfigureAwait(false); // Get stdout and stderr var stdOut = stdOutBuffer.ToString(); var stdErr = stdErrBuffer.ToString(); return(new ExecutionOutput(process.ExitCode, stdOut, stdErr, startTime, exitTime)); } }