Single Instance ClickOnce

I’m currently working on a project where the client wants to be able to click on a link and bring up a WPF UI with relevant information and available actions. Furthermore, the client wants to be able to keep clicking links and continue to reuse the same instance of the WPF UI. We decided our best option would be to go with a ClickOnce deployment, but we were unsure of how to get the web page to talk to our application.

Getting the first instance open was easy. MSDN has a great article on how to get a query string out of your URI. Straight from the article:

private NameValueCollection GetQueryStringParameters()
{
    NameValueCollection nameValueTable = new NameValueCollection();

    if (ApplicationDeployment.IsNetworkDeployed)
    {
        string queryString = ApplicationDeployment.CurrentDeployment.ActivationUri.Query;
        nameValueTable = HttpUtility.ParseQueryString(queryString);
    }

    return (nameValueTable);
}
I was also aware that you can use a Mutex to synchronize your processes. It’s a much more elegant solution than a process table search. The only question was how do we pass data from one instance of my application to another? A common solution I saw was to defer to native methods and pass strings around that way, but I felt this was too inflexible of a solution, and it requires full trust to boot. Our particular application requires full trust anyway, but in the spirit of getting things done right, we decided to give WCF [[Inter-process communication IPC]] a shot, and it definitely paid off.

My first proof of concept was much more messy than this, but I’ll spare you the clutter and discuss my better organized version. In any case, you will need to reference System.Deployment, System.ServiceModel, and System.Web.

I decided that I wanted to completely encapsulate the WCF and process synchronization logic, so I setup a class called ApplicationInstanceMonitor<T> : IApplicationMonitor<T>, where T is the type of the message you want to send with IPC. I decorated it with the following service behavior so we can use a singleton instance. Usually I’d go with a re-entrant concurrency mode, but since we won’t be handling very many requests, one at a time is sufficient.

[ServiceBehavior(
	InstanceContextMode = InstanceContextMode.Single,
	ConcurrencyMode = ConcurrencyMode.Single)]

Its implemented interface is pretty simple:

[ServiceContract]
public interface IApplicationInstanceMonitor<T>
{
	[OperationContract(IsOneWay = true)]
	void NotifyNewInstance(T message);
}

Next I created a method to deal with the Mutex, take action to setup IPC as necessary, and report its status.

private readonly string _mutexName; // Set by constructor
private Mutex _processLock;

public bool Assert()
{
	if (_processLock != null)
		throw new InvalidOperationException("Assert() has already been called.");

	bool created;
	_processLock = new Mutex(true, _mutexName, out created);

	if (created)
		StartIpcServer();
	else
		ConnectToIpcServer();

	return created;
}

For the WCF setup, I simply needed a URI to bind to (set or generated by the constructor) and a binding:

_binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport);

So connecting to WCF was rudimentary:

private readonly Uri _ipcUri; // Set by constructor
private readonly NetNamedPipeBinding _binding; // Set as above
private ServiceHost _ipcServer;
private ChannelFactory<IApplicationInstanceMonitor<T>> _channelFactory;
private IApplicationInstanceMonitor _ipcChannel;

private void StartIpcServer()
{
	_ipcServer = new ServiceHost(this, _ipcUri);
	_ipcServer.AddServiceEndpoint(typeof(IApplicationInstanceMonitor), _binding, _ipcUri);

	_ipcServer.Open();

	_ipcChannel = this;
}

private void ConnectToIpcServer()
{
	_channelFactory = new ChannelFactory<IApplicationInstanceMonitor<T>>(
		_binding, new EndpointAddress(_ipcUri));
	_ipcChannel = _channelFactory.CreateChannel();
}

Now all that’s left to do in our class is expose out the useful methods. I use an explicitly implemented service contract interface to deal with the server-side, since that’s what WCF will call into and the regular implementation for the client side.

public event EventHandler<NewInstanceCreatedEventArgs<T>> NewInstanceCreated;

// NOTE: This is not a subclass, but here for ease of viewing.
public class NewInstanceCreatedEventArgs<T> : EventArgs
{
	public NewInstanceCreatedEventArgs(T message)
		: base()
	{
		Message = message;
	}

	public T Message { get; private set; }
}

public void NotifyNewInstance(T message)
{
	// Client side

	if (_ipcChannel == null)
		throw new InvalidOperationException("Not connected to IPC Server.");

	_ipcChannel.NotifyNewInstance(message);
}

void IApplicationInstanceMonitor.NotifyNewInstance(T message)
{
	// Server side

	if (NewInstanceCreated != null)
		NewInstanceCreated(this, new NewInstanceCreatedEventArgs(message));
}

There it is! Now all we need to hook it up is in our App.xaml.cs (assuming you’re doing WPF)

private Window1 _window; // Set by constructor
protected override void OnStartup(StartupEventArgs e)
{
	if (_instanceMonitor.Assert())
	{
		// This is the only instance.

		_instanceMonitor.NewInstanceCreated += OnNewInstanceCreated;

		_window.Show();
	}
	else
	{
		// Defer to another instance.

		_instanceMonitor.NotifyNewInstance(new MyMessage { QueryString = GetQueryString() });

		Shutdown();
	}
}

private void OnNewInstanceCreated(object sender, NewInstanceCreatedEventArgs e)
{
	// Handle your message here

	_window.Activate();
}

public string GetQueryString()
{
	return ApplicationDeployment.IsNetworkDeployed ?
		ApplicationDeployment.CurrentDeployment.ActivationUri.Query :
		string.Empty;
}

public void HandleQueryString(string query)
{
	var args = HttpUtility.ParseQueryString(query);

	_window.Message = args["message"] ?? "No message provided";
}

[Serializable]
public class MyMessage
{
	public string QueryString { get; set; }
}

And now we’re done. Just a bit of HTML and you’ll be communicating with your ClickOnce in no time!

Edit: This is now on GitHub!