using System.Diagnostics;
using System.Text;
namespace Amazon.Common.DotNetCli.Tools
{
///
/// This class represents an instance of a process being executed, set up to capture
/// STDOUT and STDERR and expose as properties. Once it's created, you can execute
/// Run multiple times.
///
public class ProcessInstance
{
///
/// Results of process execution
///
public struct ProcessResults
{
///
/// True if process executed within timout
///
public bool Executed { get; set; }
///
/// Non-zero upon success
///
public int? ExitCode { get; set; }
///
/// Output captured from STDOUT
///
public string Output { get; set; }
///
/// Output captured from STDERR or other error information
///
public string Error { get; set; }
}
private readonly ProcessStartInfo _info;
///
/// Set up ProcessStartInfo, forcing values required to capture STDOUT and STDERR
///
///
public ProcessInstance(ProcessStartInfo info)
{
_info = info;
_info.RedirectStandardOutput = true;
_info.RedirectStandardError = true;
_info.UseShellExecute = false;
}
///
/// Run the process
///
///
/// Process instance execution results
public ProcessResults Run(int timeoutInMilliseconds = 60000)
{
var stdout = new StringBuilder();
var stderr = new StringBuilder();
using (var proc = new Process())
{
proc.StartInfo = _info;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived += (_, e) =>
{
if (!string.IsNullOrEmpty(e.Data)) stdout.Append(e.Data);
};
proc.ErrorDataReceived += (_, e) =>
{
if (!string.IsNullOrEmpty(e.Data)) stderr.Append(e.Data);
};
bool executed;
int? exitCode;
string output;
string error;
if (proc.Start())
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
executed = proc.WaitForExit(timeoutInMilliseconds);
if (executed)
proc.WaitForExit(); // this ensures STDOUT is completely captured
else
stderr.Append($"{(stderr.Length > 0 ? "\n" : "")}Timeout waiting for process");
exitCode = proc.ExitCode;
output = stdout.ToString();
error = stderr.ToString();
}
else
{
executed = false;
exitCode = null;
output = "";
error = "Unable to launch process";
}
return new ProcessResults()
{
Executed = executed,
ExitCode = exitCode,
Output = output,
Error = error
};
}
}
}
///
/// This class is a factory that generates executed Processes
///
public class ProcessFactory: IProcessFactory
{
public static readonly IProcessFactory Default = new ProcessFactory();
///
/// Launch the process described by "info" and return the execution results.
///
///
///
///
public ProcessInstance.ProcessResults RunProcess(ProcessStartInfo info, int timeout = 60000)
{
return new ProcessInstance(info).Run(timeout);
}
}
public interface IProcessFactory
{
ProcessInstance.ProcessResults RunProcess(ProcessStartInfo info, int timeout = 60000);
}
}