Process Executor

Description

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

Download From...




Class Diagram

Process Executor - Class Diagram


Features

  • Execute external applications.
  • Configurable working directory.
  • Accept process arguments.
  • Abort created process and it's 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 use 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 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)
                {
                    // Process already exited.
                }
            }
    

    Subclass

    ProcessExecutor can be use 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.

        /// <summary>
        /// Windows Installer Client (msiexec.exe).
        /// Uninstallation require the original MSI package.
        /// </summary>
        public class WindowsInstallerClient : ProcessExecutor
        {
            private string LogFile = "_msiexec.log";
     
            /// <summary>
            /// Constructor
            /// </summary>
            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());
            }
        }


      Popular posts from this blog

      (AGauge) WinForms Gauge Control - Discontinued

      C# DSP Simulation - DSP Lab

      WinForms Controls