// Dapplo - building blocks for desktop applications // Copyright (C) 2015-2016 Dapplo // // For more information see: http://dapplo.net/ // Dapplo repositories are hosted on GitHub: https://github.com/dapplo // // This file is part of Dapplo.Addons // // Dapplo.Addons is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Dapplo.Addons is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have Config a copy of the GNU Lesser General Public License // along with Dapplo.Addons. If not, see . #region using using log4net; using System; using System.Security.AccessControl; using System.Security.Principal; using System.Threading; #endregion namespace Greenshot.Helpers { /// /// This protects your resources or application from running more than once /// Simplifies the usage of the Mutex class, as described here: /// https://msdn.microsoft.com/en-us/library/System.Threading.Mutex.aspx /// public class ResourceMutex : IDisposable { private static readonly ILog Log = LogManager.GetLogger(typeof(DestinationHelper)); private readonly string _mutexId; private readonly string _resourceName; private Mutex _applicationMutex; /// /// Private constructor /// /// /// private ResourceMutex(string mutexId, string resourceName = null) { _mutexId = mutexId; _resourceName = resourceName ?? "some resource"; } /// /// Test if the Mutex was created and locked. /// public bool IsLocked { get; set; } /// /// Create a ResourceMutex for the specified mutex id and resource-name /// /// ID of the mutex, preferably a Guid as string /// Name of the resource to lock, e.g your application name, usefull for logs /// true to have a global mutex see: https://msdn.microsoft.com/en-us/library/bwe34f1k.aspx public static ResourceMutex Create(string mutexId, string resourceName = null, bool global = false) { var applicationMutex = new ResourceMutex((global ? @"Global\" : @"Local\") + mutexId, resourceName); applicationMutex.Lock(); return applicationMutex; } /// /// This tries to get the Mutex, which takes care of having multiple instances running /// /// true if it worked, false if another instance is already running or something went wrong public bool Lock() { Log.DebugFormat("{0} is trying to get Mutex {1}", _resourceName, _mutexId); IsLocked = true; // check whether there's an local instance running already, but use local so this works in a multi-user environment try { // Added Mutex Security, hopefully this prevents the UnauthorizedAccessException more gracefully var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); var mutexsecurity = new MutexSecurity(); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow)); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny)); mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny)); bool createdNew; // 1) Create Mutex _applicationMutex = new Mutex(true, _mutexId, out createdNew, mutexsecurity); // 2) if the mutex wasn't created new get the right to it, this returns false if it's already locked if (!createdNew && !_applicationMutex.WaitOne(100, false)) { Log.InfoFormat("{0} is already in use, mutex {1} is NOT locked for the caller", _resourceName, _mutexId); IsLocked = false; // Clean up _applicationMutex.Close(); _applicationMutex = null; } else { Log.InfoFormat(createdNew ? "{0} has created & claimed the mutex {1}" : "{0} has claimed the mutex {1}", _resourceName, _mutexId); } } catch (AbandonedMutexException e) { // Another instance didn't cleanup correctly! // we can ignore the exception, it happend on the "waitone" but still the mutex belongs to us Log.WarnFormat("{0} didn't cleanup correctly, but we got the mutex {1}.", _resourceName, _mutexId); Log.Warn(e); } catch (UnauthorizedAccessException e) { Log.ErrorFormat("{0} is most likely already running for a different user in the same session, can't create/get mutex {1} due to error.", _resourceName, _mutexId); Log.Error(e); IsLocked = false; } catch (Exception ex) { Log.ErrorFormat("Problem obtaining the Mutex {1} for {0}, assuming it was already taken!", _resourceName, _mutexId); Log.Error(ex); IsLocked = false; } return IsLocked; } #region IDisposable Support // To detect redundant Dispose calls private bool _disposedValue; /// /// The real disposing code /// /// true if dispose is called, false when the finalizer is called protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (_applicationMutex != null) { try { _applicationMutex.ReleaseMutex(); _applicationMutex = null; Log.InfoFormat("Released Mutex {0} for {1}", _mutexId, _resourceName); } catch (Exception ex) { Log.ErrorFormat("Error releasing Mutex {0} for {1}", _mutexId, _resourceName); Log.Error(ex); } } _disposedValue = true; } } /// /// Make sure the ApplicationMutex is disposed when the finalizer is called /// ~ResourceMutex() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(false); } /// /// The dispose interface, which calls Dispose(true) to signal that dispose is called. /// public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); GC.SuppressFinalize(this); } #endregion } }