Sometimes I need to prevent multiple instances of an application from running at the same time. Before .NET came along, I just dropped the code for that into the application itself. Now, using .NET, I can hang features like that off the IHost
or IHostBuilder
objects themselves, using extension methods. That way, the logic isn’t cluttering up my application startup, but, I can also still enjoy the extra functionality without any bother.
Here is a listing for the method I wrote to prevent multiple execution of a program:
public static bool RunOnce<TProgram>( this IHost host, Action<IHost> action ) where TProgram : class { Guard.Instance().ThrowIfNull(host, nameof(host)) .ThrowIfNull(action, nameof(action)); var appName = AppDomain.CurrentDomain.FriendlyNameEx(true); var appVersion = typeof(TProgram).Assembly.ReadFileVersion(); Console.Title = $"{appName} - {appVersion}"; var mutexName = $"Global\\{{{appName.Replace('\\', '_').Replace(':', '_')}}}"; using (var mutex = new Mutex(false, mutexName)) { try { if (mutex.WaitOne(TimeSpan.FromSeconds(1), false)) { try { action.Invoke(host); return true; } finally { mutex.ReleaseMutex(); } } } catch (AbandonedMutexException ex) { if (null != ex.Mutex) { ex.Mutex.ReleaseMutex(); } action.Invoke(host); } } return false; }
Let’s walk through what’s going on. The method starts by validating the incoming parameters. After that, it sets the console’s title to the friendly name and version of the application …
You know, as I write this, I realize that setting the console’s title really has nothing to do with preventing multiple instances from running. I’ll go back and try to find a better place for that code. I know why it ended up there – it was for a specific project – but it needs to be moved. No worries though, easily fixed …
The next thing we do is create a safe name for a Mutex
object. What do I mean by safe name? Well, it turns out, some names will cause runtime errors if they are used for Mutex
objects, in Windows. For instance, anything with ‘\’ or ‘:’ will create drama. Why? I can only surmise it’s because the Mutex
is using some sort of file, under the covers, that gets mapped to that name, and those symbols must mess up the path, for that file. Dunno, that’s just a guess on my part. What I do know is, we don’t want to allow those characters in the name.
In case anyone doesn’t know, a Mutex
is a mutual exclusion object that controls concurrent access to a resource. In other words, a Mutex
prevents a resource from being used by more than one thing, at a time. In our case, the ‘resource’ is our application, and the ‘thing’ are other instances of our application that are trying to run at the same time. So, we’ll use the Mutex
to ensure that we run one, and only one instance of our application, at any one time.
Next, we create a Mutex
object, using our safe name. After that, we call the WaitOne
method, on the Mutex
. That tells the Mutex
we want access to the protected resource. If WaitOne
returns true, we can assume that we have access to the resource. That’s why we use the try/finally block, because one way or the other, we need to give up control of the Mutex
when we’re done. Inside the try/finally we invoke the action
delegate. When the delegate returns, we call ReleaseMutex
, on the Mutex
object, to release our hold on the Mutex
.
Notice that the try/finally blocks are actually nested here. That’s because there is a condition that we need to be aware of, when using a Mutex
object. That condition is where the Operating System thinks the Mutex
was captured and then, never released. It’s called an ‘abandoned mutex’, and it happens for any number of different reasons. The thing is, when Windows thinks a Mutex
is abandoned, it will throw an exception that effectively gives access to the next process that asks for the object – but, it gives access in a way that also says, ‘this object was abandoned before, and now you are responsible for cleaning it up.’ If, or when, we catch that exception, we check the Mutex
property, on the AbandonedMutexException
object, and if anything is there, we call ReleaseMutex
to cleanup the object.
Once we’ve cleaned up the Mutex
, it’s no different than if we got control of the object without all the fuss and drama. In either case we still want to invoke the action
delegate, to run our application, so we call the Invoke
method on the action
delegate.
Having covered the internals of the method, here is how I typically call it:
Host.CreateDefaultBuilder() .Build() .RunOnce(host => { // TODO : put logic here. });
Where ‘Put Logic here’ would be my application logic.
I didn’t mention, but I also have an async version of this extension method, called RunOnceAsync
.
Everything I covered in the blog post is part of the CG.Hosting NUGET package. The source is available HERE. The package itself can be downloaded HERE.
Thanks for reading!
Photo by Macau Photo Agency on Unsplash