Skip to content

Instantly share code, notes, and snippets.

@AlexMAS
Last active December 9, 2021 11:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save AlexMAS/f2dc6c0527646fd0284f34dafdfcdc21 to your computer and use it in GitHub Desktop.
Save AlexMAS/f2dc6c0527646fd0284f34dafdfcdc21 to your computer and use it in GitHub Desktop.
The right way to run external process in .NET
public static class ProcessHelper
{
public static ProcessResult ExecuteShellCommand(string command, string arguments, int timeout)
{
var result = new ProcessResult();
using (var process = new Process())
{
process.StartInfo.FileName = command;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
var outputBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
using (var outputCloseEvent = new AutoResetEvent(false))
using (var errorCloseEvent = new AutoResetEvent(false))
{
var copyOutputCloseEvent = outputCloseEvent;
process.OutputDataReceived += (s, e) =>
{
// Output stream is closed (process completed)
if (string.IsNullOrEmpty(e.Data))
{
copyOutputCloseEvent.Set();
}
else
{
outputBuilder.AppendLine(e.Data);
}
};
var copyErrorCloseEvent = errorCloseEvent;
process.ErrorDataReceived += (s, e) =>
{
// Error stream is closed (process completed)
if (string.IsNullOrEmpty(e.Data))
{
copyErrorCloseEvent.Set();
}
else
{
errorBuilder.AppendLine(e.Data);
}
};
bool isStarted;
try
{
isStarted = process.Start();
}
catch (Exception error)
{
result.Completed = true;
result.ExitCode = -1;
result.Output = error.Message;
isStarted = false;
}
if (isStarted)
{
// Read the output stream first and then wait because deadlocks are possible
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (process.WaitForExit(timeout)
&& outputCloseEvent.WaitOne(timeout)
&& errorCloseEvent.WaitOne(timeout))
{
result.Completed = true;
result.ExitCode = process.ExitCode;
if (process.ExitCode != 0)
{
result.Output = $"{outputBuilder}{errorBuilder}";
}
}
else
{
try
{
// Kill hung process
process.Kill();
}
catch
{
}
}
}
}
}
return result;
}
public struct ProcessResult
{
public bool Completed;
public int? ExitCode;
public string Output;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment