using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Instances.Exceptions;
using Microsoft.CodeAnalysis;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Malte Rosenbjerg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Rosenbjerg Softworks")]
[assembly: AssemblyDescription("A .NET Standard Process wrapper with an elegant API, for both asyncronous and syncronous use, providing both Events and support for Tasks with cancellation support")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Instances")]
[assembly: AssemblyTitle("Instances")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/rosenbjerg/Instances")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
}
namespace Instances
{
public static class Instance
{
public static IProcessInstance Start(string path, string arguments = "", EventHandler<string>? outputHandler = null, EventHandler<string>? errorHandler = null)
{
return Start(new ProcessStartInfo
{
FileName = path,
Arguments = arguments
}, outputHandler, errorHandler);
}
public static IProcessInstance Start(ProcessStartInfo startInfo, EventHandler<string>? outputHandler, EventHandler<string>? errorHandler)
{
ProcessArguments processArguments = new ProcessArguments(startInfo);
if (outputHandler != null)
{
processArguments.OutputDataReceived += outputHandler;
}
if (errorHandler != null)
{
processArguments.ErrorDataReceived += errorHandler;
}
return processArguments.Start();
}
public static IProcessResult Finish(string path, string arguments = "", EventHandler<string>? outputHandler = null, EventHandler<string>? errorHandler = null)
{
return Finish(new ProcessStartInfo
{
FileName = path,
Arguments = arguments
}, outputHandler, errorHandler);
}
public static IProcessResult Finish(ProcessStartInfo startInfo, EventHandler<string>? outputHandler = null, EventHandler<string>? errorHandler = null)
{
using IProcessInstance processInstance = Start(startInfo, outputHandler, errorHandler);
return processInstance.WaitForExit();
}
public static Task<IProcessResult> FinishAsync(string path, string arguments = "", CancellationToken cancellationToken = default(CancellationToken), EventHandler<string>? outputHandler = null, EventHandler<string>? errorHandler = null)
{
return FinishAsync(new ProcessStartInfo
{
FileName = path,
Arguments = arguments
}, cancellationToken, outputHandler, errorHandler);
}
public static async Task<IProcessResult> FinishAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default(CancellationToken), EventHandler<string>? outputHandler = null, EventHandler<string>? errorHandler = null)
{
using IProcessInstance instance = Start(startInfo, outputHandler, errorHandler);
return await instance.WaitForExitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
}
public interface IProcessInstance : IDisposable
{
IReadOnlyCollection<string> OutputData { get; }
IReadOnlyCollection<string> ErrorData { get; }
event EventHandler<IProcessResult>? Exited;
event EventHandler<string>? OutputDataReceived;
event EventHandler<string>? ErrorDataReceived;
Task SendInputAsync(string input);
void SendInput(string input);
Task<IProcessResult> WaitForExitAsync(CancellationToken cancellationToken = default(CancellationToken));
IProcessResult WaitForExit();
IProcessResult Kill();
}
public interface IProcessResult
{
int ExitCode { get; }
IReadOnlyList<string> OutputData { get; }
IReadOnlyList<string> ErrorData { get; }
}
public class ProcessArguments
{
private readonly ProcessStartInfo _processStartInfo;
public bool IgnoreEmptyLines { get; set; }
public int DataBufferCapacity { get; set; } = int.MaxValue;
public event EventHandler<IProcessResult>? Exited;
public event EventHandler<string>? OutputDataReceived;
public event EventHandler<string>? ErrorDataReceived;
public ProcessArguments(string path, string arguments)
: this(new ProcessStartInfo
{
FileName = path,
Arguments = arguments
})
{
}
public ProcessArguments(ProcessStartInfo processStartInfo)
{
_processStartInfo = processStartInfo;
}
public IProcessInstance Start()
{
_processStartInfo.CreateNoWindow = true;
_processStartInfo.UseShellExecute = false;
_processStartInfo.RedirectStandardOutput = true;
_processStartInfo.RedirectStandardInput = true;
_processStartInfo.RedirectStandardError = true;
Process process = new Process
{
StartInfo = _processStartInfo,
EnableRaisingEvents = true
};
ProcessInstance processInstance = new ProcessInstance(process, IgnoreEmptyLines, DataBufferCapacity);
processInstance.Exited += this.Exited;
processInstance.OutputDataReceived += this.OutputDataReceived;
processInstance.ErrorDataReceived += this.ErrorDataReceived;
try
{
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return processInstance;
}
catch (Win32Exception ex) when (ex.Message.Contains("The system cannot find the file specified") || ex.Message.Contains("No such file or directory"))
{
throw new InstanceFileNotFoundException(_processStartInfo.FileName, ex);
}
}
}
public static class ProcessArgumentsExtensions
{
public static IProcessResult StartAndWaitForExit(this ProcessArguments processArguments)
{
using IProcessInstance processInstance = processArguments.Start();
return processInstance.WaitForExit();
}
public static async Task<IProcessResult> StartAndWaitForExitAsync(this ProcessArguments processArguments, CancellationToken cancellationToken = default(CancellationToken))
{
using IProcessInstance instance = processArguments.Start();
return await instance.WaitForExitAsync(cancellationToken);
}
}
public class ProcessInstance : IProcessInstance, IDisposable
{
private readonly bool _ignoreEmptyLines;
private readonly int _dataBufferCapacity;
private readonly Process _process;
private readonly TaskCompletionSource<bool> _mainTask = new TaskCompletionSource<bool>();
private readonly TaskCompletionSource<bool> _stdoutTask = new TaskCompletionSource<bool>();
private readonly TaskCompletionSource<bool> _stderrTask = new TaskCompletionSource<bool>();
private readonly Queue<string> _outputData = new Queue<string>();
private readonly Queue<string> _errorData = new Queue<string>();
public IReadOnlyCollection<string> OutputData => _outputData.ToList().AsReadOnly();
public IReadOnlyCollection<string> ErrorData => _errorData.ToList().AsReadOnly();
public event EventHandler<IProcessResult>? Exited;
public event EventHandler<string>? OutputDataReceived;
public event EventHandler<string>? ErrorDataReceived;
internal ProcessInstance(Process process, bool ignoreEmptyLines, int dataBufferCapacity)
{
process.OutputDataReceived += ReceiveOutput;
process.ErrorDataReceived += ReceiveError;
process.Exited += ReceiveExit;
_process = process;
_ignoreEmptyLines = ignoreEmptyLines;
_dataBufferCapacity = dataBufferCapacity;
}
public async Task SendInputAsync(string input)
{
ThrowIfProcessExited();
await _process.StandardInput.WriteAsync(input).ConfigureAwait(continueOnCapturedContext: false);
await _process.StandardInput.FlushAsync().ConfigureAwait(continueOnCapturedContext: false);
}
public void SendInput(string input)
{
ThrowIfProcessExited();
_process.StandardInput.Write(input);
_process.StandardInput.Flush();
}
public IProcessResult Kill()
{
try
{
_process.Kill();
return GetResult();
}
catch (InvalidOperationException innerException)
{
throw new InstanceProcessAlreadyExitedException(innerException);
}
}
public async Task<IProcessResult> WaitForExitAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (cancellationToken != default(CancellationToken))
{
cancellationToken.Register(delegate
{
_process.Kill();
});
}
await _mainTask.Task.ConfigureAwait(continueOnCapturedContext: false);
return GetResult();
}
public IProcessResult WaitForExit()
{
try
{
_process.WaitForExit();
return GetResult();
}
catch (SystemException innerException)
{
throw new InstanceProcessAlreadyExitedException(innerException);
}
}
public void Dispose()
{
_process.Dispose();
}
private void ReceiveExit(object sender, EventArgs e)
{
object sender2 = sender;
Task.WhenAll<bool>(_stdoutTask.Task, _stderrTask.Task).ContinueWith(delegate
{
this.Exited?.Invoke(sender2, GetResult());
return _mainTask.TrySetResult(result: true);
});
}
private void ReceiveOutput(object _, DataReceivedEventArgs e)
{
AddData(_outputData, e.Data, this.OutputDataReceived, _stdoutTask);
}
private void ReceiveError(object _, DataReceivedEventArgs e)
{
AddData(_errorData, e.Data, this.ErrorDataReceived, _stderrTask);
}
private void AddData(Queue<string> dataList, string? data, EventHandler<string>? eventHandler, TaskCompletionSource<bool> taskCompletionSource)
{
if (data == null)
{
taskCompletionSource.TrySetResult(result: true);
}
else if (!_ignoreEmptyLines || !(data == string.Empty))
{
dataList.Enqueue(data);
for (int i = 0; i < dataList.Count - _dataBufferCapacity; i++)
{
dataList.Dequeue();
}
eventHandler?.Invoke(this, data);
}
}
private IProcessResult GetResult()
{
return new ProcessResult(_process.HasExited ? _process.ExitCode : (-100), _outputData.ToArray(), _errorData.ToArray());
}
private void ThrowIfProcessExited()
{
if (_process.HasExited)
{
throw new InstanceProcessAlreadyExitedException();
}
}
}
public class ProcessResult : IProcessResult
{
public int ExitCode { get; }
public IReadOnlyList<string> OutputData { get; }
public IReadOnlyList<string> ErrorData { get; }
internal ProcessResult(int exitCode, IReadOnlyList<string> outputData, IReadOnlyList<string> errorData)
{
OutputData = outputData;
ErrorData = errorData;
ExitCode = exitCode;
}
}
}
namespace Instances.Exceptions
{
public class InstanceException : Exception
{
public InstanceException(string msg, Exception innerException)
: base(msg, innerException)
{
}
}
public class InstanceFileNotFoundException : InstanceException
{
public InstanceFileNotFoundException(string fileName, Exception innerException)
: base("File not found: " + fileName, innerException)
{
}
}
public class InstanceProcessAlreadyExitedException : Exception
{
public InstanceProcessAlreadyExitedException()
: base("The process instance has already exited")
{
}
public InstanceProcessAlreadyExitedException(Exception innerException)
: base("The process instance has already exited", innerException)
{
}
}
}