tsJensen

A quest for software excellence...

Run Single Instance of Process Using Mutex

Recently I needed to assure that a process could not be started for a second time on the same machine. I had done this before with a mutex but rather than rummaging through old code on my own personal machine, I did the expedient thing and found this question on Stack Overflow.

I liked the answer as many others did and after a few minutes of tweaking came up with this useful adaptation. I share it here in part to help others but mostly to create a permanent “note to self” as this will surely come up again in the future.

private static void Main(string[] args)
{
  // assure only one instance running
  RunExclusively(() =>
  {
    // do your exclusive stuff
  });
}

static void RunExclusively(Action action)
{
  // global mutex to prevent multiple instances from being started
  // get application GUID as defined in AssemblyInfo.cs
  string appGuid = ((GuidAttribute)
           Assembly.GetExecutingAssembly()
           .GetCustomAttributes(typeof(GuidAttribute), false)
           .GetValue(0))
           .Value;

  // 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))
  {
    // set security settings for mutex - allow everyone
    var allowEveryoneRule = new MutexAccessRule(
      new SecurityIdentifier(WellKnownSidType.WorldSid, null),
      MutexRights.FullControl, AccessControlType.Allow);
    var securitySettings = new MutexSecurity();
    securitySettings.AddAccessRule(allowEveryoneRule);
    mutex.SetAccessControl(securitySettings);

    var hasHandle = false;
    try
    {
      try
      {
        // wait to acquire for up to five seconds
        if (!mutex.WaitOne(5000, exitContext: false))
        {
          throw new TimeoutException("Timeout waiting for exclusive access");
        }
      }
      catch (AbandonedMutexException)
      {
        // mutex abandoned in another process
        // it will still get acquired
        hasHandle = true;
      }
      action(); //execute work
    }
    finally
    {
      if (hasHandle) mutex.ReleaseMutex();
    }
  }
}

If you know of a better way or see any flaws in this one, please do share.