No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProcessRunner.cs 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. using System;
  6. using System.Diagnostics;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace Microsoft.Unity.VisualStudio.Editor
  11. {
  12. internal class ProcessRunnerResult
  13. {
  14. public bool Success { get; set; }
  15. public string Output { get; set; }
  16. public string Error { get; set; }
  17. }
  18. internal static class ProcessRunner
  19. {
  20. public const int DefaultTimeoutInMilliseconds = 300000;
  21. public static ProcessStartInfo ProcessStartInfoFor(string filename, string arguments, bool redirect = true, bool shell = false)
  22. {
  23. return new ProcessStartInfo
  24. {
  25. UseShellExecute = shell,
  26. CreateNoWindow = true,
  27. RedirectStandardOutput = redirect,
  28. RedirectStandardError = redirect,
  29. FileName = filename,
  30. Arguments = arguments
  31. };
  32. }
  33. public static void Start(string filename, string arguments)
  34. {
  35. Start(ProcessStartInfoFor(filename, arguments, false));
  36. }
  37. public static void Start(ProcessStartInfo processStartInfo)
  38. {
  39. var process = new Process { StartInfo = processStartInfo };
  40. using (process)
  41. {
  42. process.Start();
  43. }
  44. }
  45. public static ProcessRunnerResult StartAndWaitForExit(string filename, string arguments, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null)
  46. {
  47. return StartAndWaitForExit(ProcessStartInfoFor(filename, arguments), timeoutms, onOutputReceived);
  48. }
  49. public static ProcessRunnerResult StartAndWaitForExit(ProcessStartInfo processStartInfo, int timeoutms = DefaultTimeoutInMilliseconds, Action<string> onOutputReceived = null)
  50. {
  51. var process = new Process { StartInfo = processStartInfo };
  52. using (process)
  53. {
  54. var sbOutput = new StringBuilder();
  55. var sbError = new StringBuilder();
  56. var outputSource = new TaskCompletionSource<bool>();
  57. var errorSource = new TaskCompletionSource<bool>();
  58. process.OutputDataReceived += (_, e) =>
  59. {
  60. Append(sbOutput, e.Data, outputSource);
  61. if (onOutputReceived != null && e.Data != null)
  62. onOutputReceived(e.Data);
  63. };
  64. process.ErrorDataReceived += (_, e) => Append(sbError, e.Data, errorSource);
  65. process.Start();
  66. process.BeginOutputReadLine();
  67. process.BeginErrorReadLine();
  68. var run = Task.Run(() => process.WaitForExit(timeoutms));
  69. var processTask = Task.WhenAll(run, outputSource.Task, errorSource.Task);
  70. if (Task.WhenAny(Task.Delay(timeoutms), processTask).Result == processTask && run.Result)
  71. return new ProcessRunnerResult {Success = true, Error = sbError.ToString(), Output = sbOutput.ToString()};
  72. try
  73. {
  74. process.Kill();
  75. }
  76. catch
  77. {
  78. /* ignore */
  79. }
  80. return new ProcessRunnerResult {Success = false, Error = sbError.ToString(), Output = sbOutput.ToString()};
  81. }
  82. }
  83. private static void Append(StringBuilder sb, string data, TaskCompletionSource<bool> taskSource)
  84. {
  85. if (data == null)
  86. {
  87. taskSource.SetResult(true);
  88. return;
  89. }
  90. sb?.Append(data);
  91. }
  92. }
  93. }