mixedreality/com.microsoft.mixedreality..../Core/Extensions/ProcessExtensions.cs

154 lines
5.6 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#if UNITY_EDITOR || !UNITY_WSA
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Process Extension class.
/// </summary>
public static class ProcessExtensions
{
/// <summary>
/// Starts a process asynchronously.
/// </summary>
/// <param name="process">This Process.</param>
/// <param name="fileName">The process executable to run.</param>
/// <param name="args">The Process arguments.</param>
/// <param name="showDebug">Should output debug code to Editor Console?</param>
/// <returns><see cref="Utilities.ProcessResult"/></returns>
public static async Task<ProcessResult> StartProcessAsync(this Process process, string fileName, string args, bool showDebug = false, CancellationToken cancellationToken = default)
{
return await StartProcessAsync(process, new ProcessStartInfo
{
FileName = fileName,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = args
}, showDebug, cancellationToken);
}
/// <summary>
/// Starts a process asynchronously.<para/>
/// </summary>
/// <remarks>The provided Process Start Info must not use shell execution, and should redirect the standard output and errors.</remarks>
/// <param name="process">This Process.</param>
/// <param name="startInfo">The Process start info.</param>
/// <param name="showDebug">Should output debug code to Editor Console?</param>
/// <returns><see cref="Utilities.ProcessResult"/></returns>
public static async Task<ProcessResult> StartProcessAsync(this Process process, ProcessStartInfo startInfo, bool showDebug = false, CancellationToken cancellationToken = default)
{
Debug.Assert(!startInfo.UseShellExecute, "Process Start Info must not use shell execution.");
Debug.Assert(startInfo.RedirectStandardOutput, "Process Start Info must redirect standard output.");
Debug.Assert(startInfo.RedirectStandardError, "Process Start Info must redirect standard errors.");
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
var processResult = new TaskCompletionSource<ProcessResult>();
var errorCodeResult = new TaskCompletionSource<string[]>();
var errorList = new List<string>();
var outputCodeResult = new TaskCompletionSource<string[]>();
var outputList = new List<string>();
process.Exited += OnProcessExited;
process.ErrorDataReceived += OnErrorDataReceived;
process.OutputDataReceived += OnOutputDataReceived;
async void OnProcessExited(object sender, EventArgs args)
{
processResult.TrySetResult(new ProcessResult(process.ExitCode, await errorCodeResult.Task, await outputCodeResult.Task));
process.Close();
process.Dispose();
}
void OnErrorDataReceived(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
errorList.Add(args.Data);
if (!showDebug)
{
return;
}
UnityEngine.Debug.LogError(args.Data);
}
else
{
errorCodeResult.TrySetResult(errorList.ToArray());
}
}
void OnOutputDataReceived(object sender, DataReceivedEventArgs args)
{
if (args.Data != null)
{
outputList.Add(args.Data);
if (!showDebug)
{
return;
}
UnityEngine.Debug.Log(args.Data);
}
else
{
outputCodeResult.TrySetResult(outputList.ToArray());
}
}
if (!process.Start())
{
if (showDebug)
{
UnityEngine.Debug.LogError("Failed to start process!");
}
processResult.TrySetResult(new ProcessResult(process.ExitCode, new[] { "Failed to start process!" }, null));
}
else
{
process.BeginOutputReadLine();
process.BeginErrorReadLine();
CancellationWatcher(process);
}
async void CancellationWatcher(Process _process)
{
await Task.Run(() =>
{
try
{
while (!_process.HasExited)
{
if (cancellationToken.IsCancellationRequested)
{
_process.Kill();
}
}
}
catch
{
// ignored
}
});
}
return await processResult.Task;
}
}
}
#endif