1

C#, .NET 4.5

I have two processes accessing a shared resource, but despite creating a Global Mutex with the approach suggested here; which should be accessible by Everyone, I am still getting a System.UnauthorizedAccessException.

I can resolve the situation by running the problematic process with elevated Administrator privileges. This suggests to me that the first locking process is not creating the Mutex with the correct AccessRule and associated SecurityIdentifier - but it appears to be - it uses the same code as above.

public class MutexLocker: IDisposable
{
    private Mutex _mutex;

    public MutexLocker ( string id )
    {
        bool createdNew;
        MutexSecurity mutexSecurity = new MutexSecurity();
        mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), 
                                                        MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));

        try
        {
            // attempt to create the mutex, with the desired DACL..
            _mutex = new Mutex(false, id, out createdNew, mutexSecurity);
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            // the mutex cannot be opened, probably because a Win32 object of a different
            // type with the same name already exists.
            throw;
        }
        catch (UnauthorizedAccessException)
        {
            // the mutex exists, but the current process or thread token does not
            // have permission to open the mutex with SYNCHRONIZE | MUTEX_MODIFY rights.
            throw;
        }
    }

    public void Dispose ()
    {
        if (_mutex != null)
        {
            _mutex.ReleaseMutex();
            _mutex.Dispose();
        }

        _mutex = null;
    }
}

I can confirm that I'm using the constructor for the Mutex which passes the AccessRule within it to avoid any race conditions.

I have also tried setting the MutexAccessRule to use MutexRights.FullControl with no change in behaviour.

EDIT: additional code tried I have also tried this different implementation with the same behaviour:

var appGuid = "{6D7C8B75-01FC-471E-B5DE-97FAAE5D8BE7}";
string mutexId = string.Format( "Global\\{{{0}}}", appGuid );

// Need a place to store a return value in Mutex() constructor call
bool createdNew;

// edited by Jeremy Wiebe to add example of setting up security for multi-user usage
// edited by 'Marc' to work also on localized systems (don't use just "Everyone") 
var allowEveryoneRule =
    new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid
                                               , null)
                       , MutexRights.FullControl
                       , AccessControlType.Allow
                       );
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);

   // edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen
using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
{
    // edited by acidzombie24
    var hasHandle = false;
    try
    {
        try
        {
            // note, you may want to time out here instead of waiting forever
            // edited by acidzombie24
            // mutex.WaitOne(Timeout.Infinite, false);
            hasHandle = mutex.WaitOne(5000, false);
            if (hasHandle == false)
                throw new TimeoutException("Timeout waiting for exclusive access");
        }
        catch (AbandonedMutexException)
        {
            // Log the fact that the mutex was abandoned in another process,
            // it will still get acquired
            hasHandle = true;
        }

        // Perform your work here.
    }
    finally
    {
        // edited by acidzombie24, added if statement
        if(hasHandle)
            mutex.ReleaseMutex();
    }
}

Any ideas?

Jayden
  • 2,656
  • 2
  • 26
  • 31
  • @mjwills; happy to put code here; but it would replicate exactly the linked to answer; seems like duplication to me ; but happy for guidance. – Jayden Mar 14 '18 at 21:49
  • @mjwills; okay, fair enough - will edit with code. Cheers. – Jayden Mar 14 '18 at 21:57

1 Answers1

0

Could that be that you do not run on Windows server? In the documention for .NET 4.7.2 it says that mutexes starting with "Global" are available on "On a server that is running Terminal Services".

sthiers
  • 3,489
  • 5
  • 34
  • 47