尝试打开互斥锁时出现UnauthorizedAccessException

时间:2013-10-23 08:32:36

标签: c# windows mutex

我在尝试打开互斥锁时遇到此异常(有时只会发生;大多数调用都成功):

System.UnauthorizedAccessException: Access to the path 'Global\4c7cddf7-e729-43b6-a75c-43f54a0ac6ac' is denied.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.Threading.Mutex.OpenExisting(String name, MutexRights rights)

我用来处理互斥锁的代码:

public class MutexLocker : IDisposable
{
    public MutexLocker(string id)
    {
        var doesNotExist = false;
        var unauthorized = false;

        try
        {
            _mutex = Mutex.OpenExisting(id, MutexRights.Synchronize | MutexRights.Modify);
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            doesNotExist = true;
        }
        catch (UnauthorizedAccessException ex)
        {
            unauthorized = true;
        }

        if (doesNotExist)
        {
            _mutex = new Mutex(false, id);

            var allowEveryoneRule = new MutexAccessRule(
                new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
            var securitySettings = new MutexSecurity();
            securitySettings.AddAccessRule(allowEveryoneRule);
            _mutex.SetAccessControl(securitySettings);
        }
        else if (unauthorized)
        {
            var tempMutex = Mutex.OpenExisting(id, MutexRights.ReadPermissions | MutexRights.ChangePermissions);
            var securitySettings = tempMutex.GetAccessControl();

            var user = Environment.UserDomainName + "\\" + Environment.UserName;

            // the rule that denied the current user the right to enter and release the mutex must be removed
            var rule = new MutexAccessRule(user, MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Deny);
            securitySettings.RemoveAccessRule(rule);

            // Now grant the correct rights
            var allowEveryoneRule = new MutexAccessRule(
                new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
            securitySettings.AddAccessRule(allowEveryoneRule);
            tempMutex.SetAccessControl(securitySettings);

            _mutex = Mutex.OpenExisting(id, MutexRights.Synchronize | MutexRights.Modify);
        }

        var success = _mutex.WaitOne(TimeSpan.FromSeconds(10), false);
        if (success == false)
        {
            _mutex.Dispose();
            _mutex = null;
            throw new ApplicationException(string.Format("Can't lock mutex (timed out): {0}", id));
        }
    }

    public void Dispose()
    {
        if (_mutex != null)
        {
            try
            {
                _mutex.ReleaseMutex();
            }
            catch (Exception exc)
            {
                Trace.WriteLine(exc);
            }

            _mutex.Dispose();
        }
    }

    private readonly Mutex _mutex;
}

互斥锁“id”是一个guid,名称冲突是不可能的 这是唯一可以创建该互斥锁的代码,它为所有用户授予对它的完全访问权限(我的进程可以在不同的用户凭据下运行)。

任何想法,为什么会发生这种未经授权的访问错误?

1 个答案:

答案 0 :(得分:17)

这门课应该可以解决你的问题:

    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;
        }
    }

唯一的注意事项是Mutex构造函数,它将尝试创建互斥锁(通过调用Win32的CreateMutex())立即将提供的安全描述符分配给命名对象。如果CreateMutex调用失败,框架将尝试使用OpenMutex打开请求SYNCHRONIZEMUTEX_MODIFY权限的命名互斥锁。

您所看到的问题是在创建互斥锁和分配安全描述符之间的简单竞争条件(至少就我注意到的那样)。使创建和安全描述符分配原子将解决该问题。