public static async Task <ProcessExecutionResult> RunAsync(this Process process, TextWriter log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan?timeout = null, Dictionary <string, string> environment_variables = null, CancellationToken?cancellation_token = null) { var stdout_completion = new TaskCompletionSource <bool> (); var stderr_completion = new TaskCompletionSource <bool> (); var exit_completion = new TaskCompletionSource <bool> (); var rv = new ProcessExecutionResult(); process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; if (environment_variables != null) { foreach (var kvp in environment_variables) { process.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value; } } process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StdoutStream) { StdoutStream.WriteLine(e.Data); StdoutStream.Flush(); } } else { stdout_completion.SetResult(true); } }; process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StderrStream) { StderrStream.WriteLine(e.Data); StderrStream.Flush(); } } else { stderr_completion.SetResult(true); } }; log.WriteLine("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); cancellation_token?.Register(() => { if (!exit_completion.Task.IsCompleted) { StderrStream.WriteLine($"Execution was cancelled."); ProcessHelper.kill(process.Id, 9); } }); new Thread(() => { if (timeout.HasValue) { if (!process.WaitForExit((int)timeout.Value.TotalMilliseconds)) { ProcessHelper.kill(process.Id, 9); process.WaitForExit((int)TimeSpan.FromSeconds(5).TotalMilliseconds); // Wait 5s for the kill to work rv.TimedOut = true; lock (StderrStream) log.WriteLine($"Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed."); } } else { process.WaitForExit(); } exit_completion.TrySetResult(true); }) { IsBackground = true, }.Start(); await Task.WhenAll(stderr_completion.Task, stdout_completion.Task, exit_completion.Task); rv.ExitCode = process.ExitCode; return(rv); }
public static async Task<ProcessExecutionResult> RunAsync (this Process process, Log log, TextWriter StdoutStream, TextWriter StderrStream, TimeSpan? timeout = null, Dictionary<string, string> environment_variables = null, CancellationToken? cancellation_token = null, bool? diagnostics = null) { var stdout_completion = new TaskCompletionSource<bool> (); var stderr_completion = new TaskCompletionSource<bool> (); var rv = new ProcessExecutionResult (); process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; if (environment_variables != null) { foreach (var kvp in environment_variables) process.StartInfo.EnvironmentVariables [kvp.Key] = kvp.Value; } process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StdoutStream) { StdoutStream.WriteLine (e.Data); StdoutStream.Flush (); } } else { stdout_completion.TrySetResult (true); } }; process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => { if (e.Data != null) { lock (StderrStream) { StderrStream.WriteLine (e.Data); StderrStream.Flush (); } } else { stderr_completion.TrySetResult (true); } }; var sb = new StringBuilder (); if (process.StartInfo.EnvironmentVariables != null) { var currentEnvironment = Environment.GetEnvironmentVariables ().Cast<System.Collections.DictionaryEntry> ().ToDictionary ((v) => (string) v.Key, (v) => (string) v.Value, StringComparer.Ordinal); var processEnvironment = process.StartInfo.EnvironmentVariables.Cast<System.Collections.DictionaryEntry> ().ToDictionary ((v) => (string) v.Key, (v) => (string) v.Value, StringComparer.Ordinal); var allKeys = currentEnvironment.Keys.Union (processEnvironment.Keys).Distinct (); foreach (var key in allKeys) { string a = null, b = null; currentEnvironment.TryGetValue (key, out a); processEnvironment.TryGetValue (key, out b); if (a != b) sb.Append ($"{key}={StringUtils.Quote (b)} "); } } sb.Append ($"{StringUtils.Quote (process.StartInfo.FileName)} {process.StartInfo.Arguments}"); log.WriteLine (sb); process.Start (); var pid = process.Id; process.BeginErrorReadLine (); process.BeginOutputReadLine (); cancellation_token?.Register (() => { if (!process.HasExited) { StderrStream.WriteLine ($"Execution was cancelled."); ProcessHelper.kill (process.Id, 9); } }); if (timeout.HasValue) { if (!await process.WaitForExitAsync (timeout.Value)) { await process.KillTreeAsync (log, diagnostics ?? true); rv.TimedOut = true; lock (StderrStream) log.WriteLine ($"{pid} Execution timed out after {timeout.Value.TotalSeconds} seconds and the process was killed."); } } await process.WaitForExitAsync (); Task.WaitAll (new Task [] { stderr_completion.Task, stdout_completion.Task }, TimeSpan.FromSeconds (1)); try { rv.ExitCode = process.ExitCode; } catch (Exception e) { rv.ExitCode = 12345678; log.WriteLine ($"Failed to get ExitCode: {e}"); } return rv; }