Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling URL Protocol without application restarts

Tags:

c#

url

protocols

My question is all about URL Protocols.

I have registered a URL Protocol called mcm, but I noticed that everytime I run it from any web browser, t creates a new instance of the application. Is there any way to handle the protocol request in an already running instance?

For example, when uTorrent is using the torrent protocol It handles the request immediately without running the app again. I couldn't really find anything interesting about it, so I am asking here...

Here is the code I use to register the protocol:

private static void RegisterUrlProtocol()
{
    UnregisterUrlProtocol();

    RegistryKey rKey = Registry.ClassesRoot.OpenSubKey(UrlProtocol, true);
    if (rKey == null)
    {
        rKey = Registry.ClassesRoot.CreateSubKey(UrlProtocol);
        rKey.SetValue("", "URL: MazCraft Protocol");
        rKey.SetValue("URL Protocol", "");

        rKey = rKey.CreateSubKey(@"shell\open\command");
        rKey.SetValue("", "\"" + Application.ExecutablePath + "\" %1");
    }

    if (rKey != null)
    {
        rKey.Close();
    }
}

And the code to read the arguments:

private static bool CheckForProtocolMessage()
{
    string[] arguments = Environment.GetCommandLineArgs();

    if (arguments.Length > 1)
    {
        string[] args = arguments[1].Split(':');
        args[1] = args[1].Replace("//", "");

        if (args[0].Trim().ToUpper() == "MCM" && args.Length > 1)
        {
            string[] actionDetail = args[1].Split('=');

            if (actionDetail[0].Trim().ToUpper() == "INSTALL" && actionDetail.Length > 1)
            {
                string id = actionDetail[1].Trim().Replace("/", "");

                Funcs.ID = id;

                return true;
            }
        }
    }

    return false;
}

Any help would be greatly appreciated :) Greetings.

like image 207
Konrad Piesiak Avatar asked Jan 31 '12 13:01

Konrad Piesiak


1 Answers

You could use a Mutex to detect an instance of the application that is already running and send the data over to the existing instance via Named Pipes.

Hope the following example helps. you can swap out the named pipes object (in this case string) for whatever serializable object you like.

NamedPipe.cs

    namespace SingleInstanceNP
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO.Pipes;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Threading;
    using System.IO;


    public class NamedPipe<T> : IDisposable
    {
        #region Attribute and Properties

        private string _pipeName;
        private NamedPipeServerStream _pipeServer;
        private bool _disposed;
        private Thread _thread;
        private bool _started;

        #endregion

        #region Constructors

        public NamedPipe(NameTypes pipeType)
        {
            _disposed = false;
            _started = false;
            _pipeName = pipeType.ToString();
            _thread = new Thread(Main);
            _thread.SetApartmentState(ApartmentState.STA);
            _thread.Name = "NamePipe: " + pipeType.ToString() + " Thread";
            _thread.IsBackground = true;
        }

        ~NamedPipe()
        {
            Dispose();
        }

        #endregion

        #region Events

        public delegate void Request(T t);
        public event Request OnRequest;

        #endregion

        #region Public Methods

        public static void Send(NameTypes pipeType, T t)
        {
            using (var npc = new NamedPipeClientStream(".", pipeType.ToString(), PipeDirection.Out))
            {
                var bf = new BinaryFormatter();
                npc.Connect();
                bf.Serialize(npc, t);
            }
        }

        public static T Recieve(NameTypes pipeType)
        {
            using (var nps = new NamedPipeServerStream(pipeType.ToString(), PipeDirection.In))
            {
                return Recieve(nps);
            }
        }

        public void Start()
        {
            if (!_disposed && !_started)
            {
                _started = true;
                _thread.Start();
            }
        }

        public void Stop()
        {
            _started = false;

            if (_pipeServer != null)
            {
                _pipeServer.Close();
                // disposing will occur on thread
            }
        }

        public void Dispose()
        {
            _disposed = true;
            Stop();

            if (OnRequest != null)
                OnRequest = null;
        }

        #endregion

        private void Main()
        {
            while (_started && !_disposed)
            {
                try
                {
                    using (_pipeServer = new NamedPipeServerStream(_pipeName))
                    {
                        T t = Recieve(_pipeServer);

                        if (OnRequest != null && _started)
                            OnRequest(t);
                    }
                }
                catch (ThreadAbortException)
                { }
                catch (System.IO.IOException iox)
                {
                    Console.WriteLine("ERROR: {0}", iox.Message);
                    Thread.Sleep(TimeSpan.FromSeconds(30));
                }
                catch (Exception ex)
                {
                    Console.WriteLine("ERROR: {0}", ex.Message);
                    return;
                }
            }
        }

        private static T Recieve(NamedPipeServerStream nps)
        {
            var bf = new BinaryFormatter();

            try
            {
                nps.WaitForConnection();

                var obj = bf.Deserialize(nps);

                if (obj is T)
                    return (T)obj;
            }
            // Catch the IOException that is raised if the pipe is
            // broken or disconnected.
            catch (IOException e)
            {
                Console.WriteLine("ERROR: {0}", e.Message);
            }
            return default(T);
        }

        #region Enums

        public enum NameTypes
        {
            PipeType1
        }

        #endregion
    }
}

Program.cs Please give credit for the APP GUID to What is a good pattern for using a Global Mutex in C#?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;

namespace SingleInstanceNP
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // get application GUID as defined in AssemblyInfo.cs
            string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();

            // unique id for global mutex - Global prefix means it is global to the machine
            string mutexId = string.Format("Global\\{{{0}}}", appGuid);

            using (var mutex = new Mutex(false, mutexId))
            {
                try
                {
                    if (!mutex.WaitOne(0, false))
                    {
                        //signal existing app via named pipes

                        NamedPipe<string>.Send(NamedPipe<string>.NameTypes.PipeType1, "test");

                        Environment.Exit(0);
                    }
                    else
                    {
                        // handle protocol with this instance   
                        Application.Run(new Form1());

                    }
                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
        }
    }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SingleInstanceNP
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // start listening for named pipe connections
            var namedPipeString = new NamedPipe<string>(NamedPipe<string>.NameTypes.PipeType1);
            namedPipeString.OnRequest += new NamedPipe<string>.Request(namedPipeString_OnRequest);
            namedPipeString.Start();
        }

        void namedPipeString_OnRequest(string t)
        {
            MessageBox.Show(t);
        }
    }
}
like image 75
Aussie Ash Avatar answered Oct 07 '22 16:10

Aussie Ash