Process Creation and Coordination

In some previous posts (Out of Process IPC/TCP Remoting code, .Net Remoting (AppDomains, Out of Process, Two Way, etc..)) I showed some sample code using .Net remoting over a process boundary. As a result, I received a few questions about the process creation and how to handle the process coordination (i.e., The Host/Master process waiting on the created process startup). I apologize to those who have been waiting for another sample on the subject, but I have been pretty busy as of late.

The attached sample code shows the following;

1. How to ensure the Host Process waits on the new process creation.

a. Previously I showed a simple *hack* where the created process sleeps for a while in order to let the process creation complete.

b. A much cleaner and more reliable solution uses EventWaitHandle.

2. The created Process runs until the Host Process is terminated or the Host terminates the new process.

a. Previously I showed a simple *hack* where the created process does a Console.ReadLine() in order to halt code execution and thus cause process termination.

b. I didn’t show an example of how the created process can shutdown in the event the Host process shuts down. This scenario covers orphaned processes.

c. The solution: System.Diagnostics.Process.GetProcessById(HostPID).WaitForExit();

View Source for Host Process: Show Code ...

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Globalization;

using System.Text;

using System.Threading;

namespace SampleWaitForProcessStartup

{

    class Program

    {

        static void Main(string[] args)

        {

            String CurrentProcessInfo = String.Format(CultureInfo.InvariantCulture

                                        , "Host Process:{0}, PID:{1}"

                                        , Process.GetCurrentProcess().ProcessName

                                        , Process.GetCurrentProcess().Id.ToString());

            Console.WriteLine(CurrentProcessInfo + ", Running.");

            int TIMEOUT_SECONDS = 5;

            Guid GUID = Guid.NewGuid();

            String NewProcessArgs = String.Format(CultureInfo.InvariantCulture

                                        , "{0} {1}"

                                        , GUID.ToString()

                                        , Process.GetCurrentProcess().Id.ToString());

            Process NewProcess = new Process();

            NewProcess.StartInfo.FileName = Environment.CurrentDirectory + "\\SampleProcess.exe";

            NewProcess.StartInfo.CreateNoWindow = false;

            NewProcess.StartInfo.Arguments = NewProcessArgs;

            NewProcess.StartInfo.UseShellExecute = false;

            // Wait until the process we are creating is ready.

            // Appending a unique identifier to the external process handle which also aids in debugging.

            EventWaitHandle readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "SampleProcess:" + GUID.ToString());

            NewProcess.Start();

            // wait nSeconds for the process to start

            bool success = readyEvent.WaitOne(new TimeSpan(0, 0, TIMEOUT_SECONDS), false);

            readyEvent.Close();

            if (!success)

            {

                // Try to avoid leaving a half-baked process around if possible.

                try

                {

                    NewProcess.Kill();

                }

        catch (Exception) { }

            }

            Console.WriteLine("Press <Enter> to shut down the Host Process and in turn the New/Created process should shut down.");

            Console.ReadLine();

        }

    }

}

View Source for Created Process: Show Code ...

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Globalization;

using System.Text;

using System.Threading;

namespace SampleProcess

{

    class Program

    {

        /// <summary>

        /// This is a sample console app that is started up by a Host process (e.g., "SampleWaitForProcessStartup").

        /// It is difficult to ensure that a managed process (i.e., this app) is started completely by a Host app/process

        /// before communicating between the processes. Most people use a less determisitic aproach by putting a

        /// sleep timer in the process they are starting up.

        /// </summary>

        /// <param name="args">

        /// Arg1: A unique identifier of this process. The parm is passed on the command line to

        /// this process that is started.

        /// Arg2: PID of the Host Process. In the event the Host process shuts down, so whould we.</param>

        static int Main(string[] args)

        {

            TimeSpan waitTime = new TimeSpan(0, 0, 10);

            // We will use the convention that a process exit value of 0 == sucess.

            // This also allows command line tasks to "catch a bad process exit".

            if (args == null || args.Length != 2 || args[0].Length != 36)

                return 1;

            string GUID = args[0];

            int HostPID = Convert.ToInt32(args[1], CultureInfo.InvariantCulture);

            // Prepend "ProcessName" to unique identifier (i.e., per process) for wait handle. This is

            // also handy when debugging.

            // If you attach a VS debugger to a running process, then press the pause icon,

            // you can see local variables and the GUID that was passed to help identify the spawned process.

            string CurrentProcessName = System.Diagnostics.Process.GetCurrentProcess().ProcessName + ":" + GUID;

            String CurrentProcessInfo = String.Format(CultureInfo.InvariantCulture, "Process:{0}, PID:{1}"

                            , CurrentProcessName, Process.GetCurrentProcess().Id.ToString());

            Console.WriteLine(CurrentProcessInfo + ", Starting.");

            // Signal to the host process that we are ready for communication.

            EventWaitHandle readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, CurrentProcessName);

            // Uncomment out the next line in order to test killing this process if it does not startup within

            // the Host process requested timeout.

            //Thread.Sleep(waitTime);

            readyEvent.Set();

            readyEvent.Close();

            Console.WriteLine(CurrentProcessInfo + ", Started.");

            try

            {

                Console.WriteLine("Waiting on Host Process PID (" + HostPID.ToString() + ") to exit.");

                System.Diagnostics.Process.GetProcessById(HostPID).WaitForExit();

            }

            catch

            {

                // The host may have shut down before we could wait on it.

                Environment.Exit(1);

                return 1;

            }

            Environment.Exit(0);

            return 0;

        }

    }

}

 

WaitForProcessStartup.zip