A game about forced loneliness, made by TACStudios
at master 112 lines 3.3 kB view raw
1/*--------------------------------------------------------------------------------------------- 2 * Copyright (c) Microsoft Corporation. All rights reserved. 3 * Licensed under the MIT License. See License.txt in the project root for license information. 4 *--------------------------------------------------------------------------------------------*/ 5 6using System; 7using System.Diagnostics; 8using System.Text; 9using System.Threading; 10using System.Threading.Tasks; 11 12namespace Microsoft.Unity.VisualStudio.Editor 13{ 14 internal class ProcessRunnerResult 15 { 16 public bool Success { get; set; } 17 public string Output { get; set; } 18 public string Error { get; set; } 19 } 20 21 internal static class ProcessRunner 22 { 23 public const int DefaultTimeoutInMilliseconds = 300000; 24 25 public static ProcessStartInfo ProcessStartInfoFor(string filename, string arguments, bool redirect = true, bool shell = false) 26 { 27 return new ProcessStartInfo 28 { 29 UseShellExecute = shell, 30 CreateNoWindow = true, 31 RedirectStandardOutput = redirect, 32 RedirectStandardError = redirect, 33 FileName = filename, 34 Arguments = arguments 35 }; 36 } 37 38 public static void Start(string filename, string arguments) 39 { 40 Start(ProcessStartInfoFor(filename, arguments, false)); 41 } 42 43 public static void Start(ProcessStartInfo processStartInfo) 44 { 45 var process = new Process { StartInfo = processStartInfo }; 46 47 using (process) 48 { 49 process.Start(); 50 } 51 } 52 53 public static ProcessRunnerResult StartAndWaitForExit(string filename, string arguments, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null) 54 { 55 return StartAndWaitForExit(ProcessStartInfoFor(filename, arguments), timeoutms, onOutputReceived); 56 } 57 58 public static ProcessRunnerResult StartAndWaitForExit(ProcessStartInfo processStartInfo, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null) 59 { 60 var process = new Process { StartInfo = processStartInfo }; 61 62 using (process) 63 { 64 var sbOutput = new StringBuilder(); 65 var sbError = new StringBuilder(); 66 67 var outputSource = new TaskCompletionSource<bool>(); 68 var errorSource = new TaskCompletionSource<bool>(); 69 70 process.OutputDataReceived += (_, e) => 71 { 72 Append(sbOutput, e.Data, outputSource); 73 if (onOutputReceived != null && e.Data != null) 74 onOutputReceived(e.Data); 75 }; 76 process.ErrorDataReceived += (_, e) => Append(sbError, e.Data, errorSource); 77 78 process.Start(); 79 process.BeginOutputReadLine(); 80 process.BeginErrorReadLine(); 81 82 var run = Task.Run(() => process.WaitForExit(timeoutms)); 83 var processTask = Task.WhenAll(run, outputSource.Task, errorSource.Task); 84 85 if (Task.WhenAny(Task.Delay(timeoutms), processTask).Result == processTask && run.Result) 86 return new ProcessRunnerResult {Success = true, Error = sbError.ToString(), Output = sbOutput.ToString()}; 87 88 try 89 { 90 process.Kill(); 91 } 92 catch 93 { 94 /* ignore */ 95 } 96 97 return new ProcessRunnerResult {Success = false, Error = sbError.ToString(), Output = sbOutput.ToString()}; 98 } 99 } 100 101 private static void Append(StringBuilder sb, string data, TaskCompletionSource<bool> taskSource) 102 { 103 if (data == null) 104 { 105 taskSource.SetResult(true); 106 return; 107 } 108 109 sb?.Append(data); 110 } 111 } 112}