Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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