CodeProject
Description
ProcessExecutor
is a wrapper class for System.Diagnostic.Process
which is created with the intention to manage external process within .NET application without showing up the console window and to capture all the console output logs at the same time.
Class Diagram

Process Executor - Class Diagram
Features
- Execute external applications
- Configurable working directory
- Accept process arguments
- Abort created process and its child processes
- Thread safe, support multi-threaded execution
- Show / hide console window during process execution
- Enable / disable console output to
System.Diagnostics.Trace
- Execute process as different user
- Capture standard output and standard error from console application
- Capture output to log file
- Subclass as tool-specific executor
- Option to wait for process complete
- Notification when process completed
Usage Guide
- Assign Application, Arguments and WorkingDirectory.
- Call
Execute()
to start.
- Process Executor will start process by calling
Process.Start
internally. - Process Executor will subscribe to
OutputDataReceived
and ErrorDataReceived
events from ProcessHandler
to capture all console output. - For each captured output and error messages:
- If Output file is defined, write message to output file.
- If option
TraceLogEnabled
is set, write message to System.Diagnostics.Trace
.
- Wait for process to complete if
waitForExit
flag is set, else return immediately. - At the end of execution, return error code and console output messages as
ProcessResult
object.
- To terminate an executing process, call
Abort()
to stop process and all child processes. See section Abort for more details.
Capture Output Data
The two functions below shows how output from console window is captured in Trace and output log.
When used together Diagnostics TextBox with option ShowConsoleWindow
disabled and TraceLogEnabled
enabled, you will have all the console output from the triggered process redirect to your application. This is even better when the ProcessExecutor
is started in the worker thread.
private void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
errorDetected = true;
if (RedirectToFile) System.IO.File.AppendAllText(LogFile, e.Data + "\r\n");
else outputData.Add(e.Data);
if (TraceLogEnabled) Trace.WriteLine(Name + ": " + e.Data);
}
}
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
if (RedirectToFile) System.IO.File.AppendAllText(LogFile, e.Data + "\r\n");
else outputData.Add(e.Data);
if (TraceLogEnabled) Trace.WriteLine(Name + ": " + e.Data);
}
}
Abort
The Abort
function in ProcessExecutor
is useful to terminate the spawned process either when error is detected or decided to terminate by user. The Abort
function will terminate the process and all its child process. This is handy to terminate all the spawned process when application in terminated.
The terminate
function was not part of System.Diagnostics.Process
. We make use of ManagementObjectSearcher
to search for object with given object ID obtained from ProcessHandler
when process started. All child process created by its parents will also be terminated. This is useful when you want to terminate a batch file and all the processes spawned from the batch file.
private static void KillProcessAndChildren(int pid)
{
ManagementObjectSearcher searcher = new
ManagementObjectSearcher(
"Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
}
}
Subclass
ProcessExecutor
can be used as base class for dedicated tool such as Windows Installer (msiexec.exe) and Visual Studio (devenv.com) which provide more than just process execution.
Example: WindowsInstallerClient
with Install
and Uninstall
function.
public class WindowsInstallerClient : ProcessExecutor
{
private string LogFile = "_msiexec.log";
public WindowsInstallerClient()
{
Name = "Windows Installer (msiexec.exe)";
Application = "C:\\Windows\\System32\\msiexec.exe";
}
public void Install(string msiPackage)
{
Arguments = "/passive /I " + msiPackage + " /lei " + LogFile;
ProcessResult result = Execute();
Trace.WriteLine(System.IO.File.ReadAllText(LogFile));
Trace.WriteLine(Name + "Install completed. Result = " +
result.ExitCode.ToString());
if (result.ExitCode != 0)
throw new Exception("Install failed, exit with error code " +
result.ExitCode.ToString());
}
public void Uninstall(string msiPackage)
{
Arguments = "/passive /uninstall " + msiPackage + " /lei " + LogFile;
ProcessResult result = Execute();
Trace.WriteLine(System.IO.File.ReadAllText(LogFile));
Trace.WriteLine(Name + "Uninstall completed. Result = " +
result.ExitCode.ToString());
if (result.ExitCode != 0)
throw new Exception("Uninstall failed, exit with error code " +
result.ExitCode.ToString());
}
}