C# Pass Arguments to the first instance of a program

Sometimes you don’t want the user to run more than one instance of a program. But if the user associates a filetype with your programm it shall open the filetype in this first instance. The code below uses interprocess communication to find out if there is already another instance. It further allows the first instance to register an eventhandler which can be called from a second instance in order pass its command line parameters to the first instance.

This is how to use the code:

using System;
using System.Collections.Generic;
using System.Text;

namespace SingleInstanceApp
{
    class Program
    {
        static void myReceive(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                Console.WriteLine("MSG Received " + i.ToString() + ": " + args[i]);
            }
        }

        static void Main(string[] args)
        {
            // test if this is the first instance and register receiver, if so.
            if(SingleInstanceEnforcer.IamFirst(new SingleInstanceEnforcer.CommandLineDelegate(myReceive)))
            {
                // first instance
                while (true) {
                    System.Threading.Thread.Sleep(1000);
                }
            }
            // second instance
            else
            {
                // send command line args to running app, then terminate
                SingleInstanceEnforcer.PassCommandLine(args);
            }

            SingleInstanceEnforcer.Cleanup();
        }
    }
}

This is the SingleInstanceEnforcer class

using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Channels.Ipc;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;

namespace Common.IPC {
    /// <summary>
    /// Helper class that allows the first instance to register a CommandLineHandler which can receive the command line arguments of subsequent 
    /// instances.
    /// </summary>
    [Serializable]
    public class SingleInstanceEnforcer : MarshalByRefObject {
        private static IpcChannel _mIpcChannel;

        // used to check if this is the only instance running
        private static Mutex _mMutex;

        // some constants to setup the channel between the server (the first) and the client (subsequent) instances
        private const string PORT_NAME = "IPCDemo";
        private const string SERVICE_NAME = "IPCDelegates";
        private const string SERVICE_URL = "ipc://" + PORT_NAME + "/" + SERVICE_NAME;
        private const string UNIQUE_IDENTIFIER = "03a28812-bf16-4db2-a03f-e0fb939813db";

        // signature of the handler
        public delegate void CommandLineDelegate(string[] args);

        /// <summary>
        /// Here we can assign methods that will be called if we receive command line arguments
        /// </summary>
        static private CommandLineDelegate _mCommandLine;
        static public CommandLineDelegate CommandLineHandler {
            get {
                return _mCommandLine;
            }
            set {
                _mCommandLine = value;
            }
        }

        /// <summary>
        /// Checks if this is the first instance. If it is it does also register the CommandLineDelegate
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public static bool IsFirst(CommandLineDelegate r) {
            if (IsFirst()) {
                CommandLineHandler += r;
                return true;
            }

            return false;
        }

        /// <summary>
        /// Does just check if this is the first instance. If you want to receive command line arguments from other instances you still need
        /// to register a CommandLineHandler
        /// </summary>
        /// <returns></returns>
        public static bool IsFirst() {
            _mMutex = new Mutex(false, UNIQUE_IDENTIFIER);

            if (_mMutex.WaitOne(1, true)) {
                //We locked it! We are the first instance
                CreateInstanceChannel();
                return true;
            }

            //Not the first instance
            _mMutex.Close();
            _mMutex = null;
            return false;
        }

        /// <summary>
        /// Registers the channel for the server
        /// </summary>
        private static void CreateInstanceChannel() {
            // correct serialization of delegates
            BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider {
                TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
            };

            BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();

            // Create and register the channel
            Dictionary<string, string> properties = new Dictionary<string, string>();
            properties["portName"] = PORT_NAME;
            _mIpcChannel = new IpcChannel(properties, clientProv, serverProv);

            ChannelServices.RegisterChannel(_mIpcChannel, false);
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof(SingleInstanceEnforcer),
                SERVICE_NAME,
                WellKnownObjectMode.SingleCall);
        }

        /// <summary>
        /// Cleanup all used resources. Can be called from the server or from a client
        /// </summary>
        public static void Cleanup() {
            if (_mMutex != null) {
                _mMutex.Close();
            }

            if (_mIpcChannel != null) {
                _mIpcChannel.StopListening(null);
            }

            _mMutex = null;
            _mIpcChannel = null;
        }

        /// <summary>
        /// A client (subsequent) instance can send its Command Line parameter to the first instance (server)
        /// This method might throw an exception for various reasons (could not register the channel, object was not in channel, ...)
        /// </summary>
        /// <param name="s"></param>
        public static void PassCommandLine(string[] s) {
            IpcChannel channel = new IpcChannel("IPC_Client");
            ChannelServices.RegisterChannel(channel, false);
            SingleInstanceEnforcer ctrl = (SingleInstanceEnforcer)Activator.GetObject(typeof(SingleInstanceEnforcer), SERVICE_URL);
            ctrl.ReceiveCommandLine(s);
        }

        /// <summary>
        /// Needs to be public because otherwise it cannot be called remotely!
        /// </summary>
        /// <param name="s"></param>
        public void ReceiveCommandLine(string[] s) {
            if (_mCommandLine != null) {
                _mCommandLine(s);
            }
        }
    }
}

Leave a comment

Your email address will not be published. Required fields are marked *