2008-07-08

Single instance application

An app I am working on needs to be a single instance. It is associated with certain file extensions so that when I select a character or license file it will be imported automatically. When the user buys a character or license (etc) from the website it will be downloaded and opened, and then imported.

Obviously it is a pretty poor user experience if they have to close the app, download, close the app, download... So what I really needed was a way to have the 2nd instance of the application to invoke the first instance and pass the command line parameters. Here is a simple solution I implemented using remoting.

01: An interface

public interface ISingleInstance
{
void Execute(string[] args);
}


02: A class that implements the interface

public class SingleInstance : MarshalByRefObject, ISingleInstance
{
  private static object SyncRoot = new object();
  private static Form1 MainForm;

  static SingleInstance()
  {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
  }

  public void Execute(string[] args)
  {
    bool isNew = false;
    lock (SyncRoot)
    {
      isNew = (MainForm == null);
      if (isNew)
        MainForm = new Form1();
      MainForm.AcceptCommandLineArguments(args);
    }
    if (isNew)
      Application.Run(MainForm);
  }
}


03: And finally the remoting code in the Program.cs file itself:

static class Program
{
  private static IpcChannel IpcChannel;

  [STAThread]
  static void Main(string[] args)
  {
    bool isNew;
    using (Mutex mutex = new Mutex(true, "TheCatSatOnTheMat", out isNew))
    {
      if (isNew)
        RegisterServer();
      else
      {
        IpcChannel = new IpcChannel("Client");
        ChannelServices.RegisterChannel(IpcChannel, false);
      }
      ISingleInstance app = (ISingleInstance)Activator.GetObject(typeof(ISingleInstance), "ipc://Server/RemotingServer");
      app.Execute(args);
    }
  }

  private static void RegisterServer()
  {
    IpcChannel = new IpcChannel("Server");
    ChannelServices.RegisterChannel(IpcChannel, false);
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(SingleInstance), "RemotingServer", WellKnownObjectMode.Singleton);
  }
}


This is more of a note to myself in case I lose my small test app before I come around to implementing it into my main application :-)

No comments: