154 lines
5.6 KiB
C#
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
|