Created
December 10, 2015 19:15
-
-
Save augustoproiete/5134f24de133f7723809 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading; | |
namespace Caio.Proiete.Utils | |
{ | |
public class SilentProcessRunner : IDisposable | |
{ | |
private readonly Action<string> outputAction; | |
private readonly Action<string> errorAction; | |
private readonly Action<int> exitAction; | |
private static readonly Encoding oemEncoding; | |
private readonly CountdownEvent processExitedCountdownEvent; | |
private Process process; | |
static SilentProcessRunner() | |
{ | |
try | |
{ | |
CPINFOEX cpinfoex; | |
oemEncoding = Encoding.GetEncoding(GetCPInfoEx(1, 0, out cpinfoex) ? cpinfoex.CodePage : 850); | |
} | |
catch (Exception) | |
{ | |
oemEncoding = Encoding.UTF8; | |
} | |
} | |
public SilentProcessRunner(string executablePath, string arguments, string workingDirectory, | |
Action<string> outputAction, Action<string> errorAction, Action<int> exitAction) | |
{ | |
ExecutablePath = executablePath; | |
Arguments = arguments; | |
WorkingDirectory = workingDirectory; | |
this.outputAction = outputAction; | |
this.errorAction = errorAction; | |
this.exitAction = exitAction; | |
processExitedCountdownEvent = new CountdownEvent(4); | |
} | |
~SilentProcessRunner() | |
{ | |
Dispose(false); | |
} | |
public bool Start() | |
{ | |
if (!processExitedCountdownEvent.IsSet && processExitedCountdownEvent.CurrentCount != processExitedCountdownEvent.InitialCount) | |
{ | |
throw new InvalidOperationException("A process is already running"); | |
} | |
if (process != null) | |
{ | |
if (!process.HasExited) | |
{ | |
throw new InvalidOperationException("A process is already running"); | |
} | |
process.Dispose(); | |
} | |
try | |
{ | |
process = new Process | |
{ | |
StartInfo = | |
{ | |
FileName = ExecutablePath ?? string.Empty, | |
Arguments = Arguments ?? string.Empty, | |
WorkingDirectory = WorkingDirectory ?? string.Empty, | |
UseShellExecute = false, | |
CreateNoWindow = true, | |
RedirectStandardOutput = true, | |
RedirectStandardError = true, | |
StandardOutputEncoding = oemEncoding, | |
StandardErrorEncoding = oemEncoding, | |
} | |
}; | |
process.Exited += (sender, args) => | |
{ | |
if (exitAction != null) | |
{ | |
exitAction(((Process)sender).ExitCode); | |
} | |
processExitedCountdownEvent.Signal(); | |
}; | |
process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e) | |
{ | |
if (e.Data == null) | |
{ | |
processExitedCountdownEvent.Signal(); | |
} | |
else if (outputAction != null) | |
{ | |
outputAction(e.Data); | |
} | |
}; | |
process.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e) | |
{ | |
if (e.Data == null) | |
{ | |
processExitedCountdownEvent.Signal(); | |
} | |
else if (errorAction != null) | |
{ | |
errorAction(e.Data); | |
} | |
}; | |
process.EnableRaisingEvents = true; | |
processExitedCountdownEvent.Reset(); | |
processExitedCountdownEvent.Signal(); | |
var result = process.Start(); | |
if (result) | |
{ | |
process.BeginOutputReadLine(); | |
process.BeginErrorReadLine(); | |
} | |
return result; | |
} | |
catch (Exception exception) | |
{ | |
throw new Exception( | |
string.Format("Error when attempting to execute {0}: {1}", process.StartInfo.FileName, exception.Message), exception); | |
} | |
} | |
public void Kill() | |
{ | |
process.Kill(); | |
} | |
public void WaitForExit() | |
{ | |
process.WaitForExit(); | |
processExitedCountdownEvent.Wait(); | |
} | |
public bool WaitForExit(TimeSpan timeout) | |
{ | |
return processExitedCountdownEvent.Wait(timeout); | |
} | |
public bool WaitForExit(TimeSpan timeout, CancellationToken cancellationToken) | |
{ | |
return processExitedCountdownEvent.Wait(timeout, cancellationToken); | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
} | |
public virtual void Dispose(bool disposing) | |
{ | |
if (!disposing) | |
{ | |
return; | |
} | |
processExitedCountdownEvent.Dispose(); | |
process.Dispose(); | |
GC.SuppressFinalize(this); | |
} | |
public string ExecutablePath { get; private set; } | |
public string Arguments { get; set; } | |
public string WorkingDirectory { get; set; } | |
public bool HasExited | |
{ | |
get { return process.HasExited; } | |
} | |
public int ExitCode | |
{ | |
get { return process.ExitCode; } | |
} | |
public WaitHandle WaitHandle | |
{ | |
get { return processExitedCountdownEvent.WaitHandle; } | |
} | |
// ReSharper disable InconsistentNaming | |
// ReSharper disable FieldCanBeMadeReadOnly.Local | |
// ReSharper disable MemberCanBePrivate.Local | |
[DllImport("kernel32.dll", SetLastError = true)] | |
private static extern bool GetCPInfoEx([MarshalAs(UnmanagedType.U4)] int CodePage, [MarshalAs(UnmanagedType.U4)] int dwFlags, out CPINFOEX lpCPInfoEx); | |
[StructLayout(LayoutKind.Sequential)] | |
private struct CPINFOEX | |
{ | |
[MarshalAs(UnmanagedType.U4)] | |
public int MaxCharSize; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] | |
public byte[] DefaultChar; | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] | |
public byte[] LeadBytes; | |
public char UnicodeDefaultChar; | |
[MarshalAs(UnmanagedType.U4)] | |
public int CodePage; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] | |
public string CodePageName; | |
} | |
// ReSharper restore MemberCanBePrivate.Local | |
// ReSharper restore FieldCanBeMadeReadOnly.Local | |
// ReSharper restore InconsistentNaming | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment