2020-03-19 12:13:57 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Reactive.Subjects ;
using System.Threading.Tasks ;
using AlphaPath = Alphaleonis . Win32 . Filesystem . Path ;
namespace Wabbajack.Common
{
public class ProcessHelper
{
public enum StreamType
{
Output ,
Error ,
}
2020-04-03 23:23:13 +00:00
2020-04-10 12:58:55 +00:00
public AbsolutePath Path { get ; set ; }
2020-04-03 23:23:13 +00:00
public IEnumerable < object > Arguments { get ; set ; } = Enumerable . Empty < object > ( ) ;
2020-03-19 12:13:57 +00:00
public bool LogError { get ; set ; } = true ;
2020-04-10 12:58:55 +00:00
public readonly Subject < ( StreamType Type , string Line ) > Output = new Subject < ( StreamType Type , string ) > ( ) ;
public bool ThrowOnNonZeroExitCode { get ; set ; } = false ;
2020-03-19 12:13:57 +00:00
public ProcessHelper ( )
{
}
public async Task < int > Start ( )
{
2020-04-10 12:58:55 +00:00
var args = Arguments . Select ( arg = >
{
return arg switch
{
AbsolutePath abs = > $"\" { abs } \ "" ,
RelativePath rel = > $"\" { rel } \ "" ,
_ = > arg . ToString ( )
} ;
} ) ;
2020-03-19 12:13:57 +00:00
var info = new ProcessStartInfo
{
FileName = ( string ) Path ,
2020-04-10 12:58:55 +00:00
Arguments = string . Join ( " " , args ) ,
2020-03-19 12:13:57 +00:00
RedirectStandardError = true ,
RedirectStandardInput = true ,
RedirectStandardOutput = true ,
UseShellExecute = false ,
CreateNoWindow = true
} ;
var finished = new TaskCompletionSource < int > ( ) ;
var p = new Process
{
StartInfo = info ,
EnableRaisingEvents = true
} ;
2020-03-28 13:33:39 +00:00
EventHandler Exited = ( sender , args ) = >
2020-03-19 12:13:57 +00:00
{
finished . SetResult ( p . ExitCode ) ;
} ;
2020-03-28 13:33:39 +00:00
p . Exited + = Exited ;
2020-03-19 12:13:57 +00:00
2020-03-28 13:33:39 +00:00
DataReceivedEventHandler OutputDataReceived = ( sender , data ) = >
2020-03-19 12:13:57 +00:00
{
if ( string . IsNullOrEmpty ( data . Data ) ) return ;
Output . OnNext ( ( StreamType . Output , data . Data ) ) ;
} ;
2020-03-28 13:33:39 +00:00
p . OutputDataReceived + = OutputDataReceived ;
2020-03-19 12:13:57 +00:00
2020-03-28 13:33:39 +00:00
DataReceivedEventHandler ErrorEventHandler = ( sender , data ) = >
2020-03-19 12:13:57 +00:00
{
if ( string . IsNullOrEmpty ( data . Data ) ) return ;
Output . OnNext ( ( StreamType . Error , data . Data ) ) ;
if ( LogError )
2020-04-10 12:58:55 +00:00
Utils . Log ( $"{Path.FileName} ({p.Id}) StdErr: {data.Data}" ) ;
2020-03-19 12:13:57 +00:00
} ;
2020-03-28 13:33:39 +00:00
p . ErrorDataReceived + = ErrorEventHandler ;
2020-03-19 12:13:57 +00:00
p . Start ( ) ;
p . BeginErrorReadLine ( ) ;
p . BeginOutputReadLine ( ) ;
ChildProcessTracker . AddProcess ( p ) ;
try
{
p . PriorityClass = ProcessPriorityClass . BelowNormal ;
}
catch ( Exception )
{
// ignored
}
var result = await finished . Task ;
2020-03-28 13:33:39 +00:00
p . CancelErrorRead ( ) ;
p . CancelOutputRead ( ) ;
p . OutputDataReceived - = OutputDataReceived ;
p . ErrorDataReceived - = ErrorEventHandler ;
p . Exited - = Exited ;
2020-03-19 12:13:57 +00:00
Output . OnCompleted ( ) ;
2020-04-10 12:58:55 +00:00
if ( result ! = 0 & & ThrowOnNonZeroExitCode )
2020-05-27 12:05:55 +00:00
throw new Exception ( $"Error executing {Path} - Exit Code {result} - Check the log for more information - {string.Join(" ", args.Select(a => a!.ToString()))}" ) ;
2020-03-19 12:13:57 +00:00
return result ;
}
}
}