这个Mutex构造函数引发OutOfMemoryException异常的原因是什么?

4

在这行代码上,我得到了一个System.OutOfMemoryException异常:

mutex2 = new Mutex(true, "Name2");

以下是堆栈跟踪信息:

{"Exception of type 'System.OutOfMemoryException' was thrown."}
   at Microsoft.Win32.Win32Native.CreateMutex(SECURITY_ATTRIBUTES lpSecurityAttributes, Boolean initialOwner, String name)
   at System.Threading.Mutex.CreateMutexHandle(Boolean initiallyOwned, String name, SECURITY_ATTRIBUTES securityAttribute, SafeWaitHandle& mutexHandle)
   at System.Threading.Mutex.MutexTryCodeHelper.MutexTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.Mutex.CreateMutexWithGuaranteedCleanup(Boolean initiallyOwned, String name, Boolean& createdNew, SECURITY_ATTRIBUTES secAttrs)
   at System.Threading.Mutex..ctor(Boolean initiallyOwned, String name, Boolean& createdNew, MutexSecurity mutexSecurity)
   at System.Threading.Mutex..ctor(Boolean initiallyOwned, String name)
   at Foo.FooDefinitions.FooManager.FooForm.FooForm_Load(Object sender, EventArgs e) in c:\tfs\DWS\TRUNK\DEV\FooDefinitions\FooManager\FooForm.cs:line 92

只有在我使用模拟身份时才会发生这种情况。如果不使用模拟身份(在我的普通Windows帐户上运行),它将正常运行。模拟身份的示例如下:

    if (!NativeMethods.LogonUser(userName, domainName, password, 2, 0, ref this._tokenHandle)) // [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    {
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    this._impersonatedUser = new WindowsIdentity(this._tokenHandle).Impersonate();

编辑: 为了详细说明,我正在对旧代码创建自动化测试。如果可能的话,我会删除对互斥锁的使用。我目前正在调查 Mutex 构造函数中的 SecurityCriticalAttribute。

编辑2: 这里是完整的代码示例:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;

namespace ReinierDG.MutexTesting
{
    [TestClass]
    public class MutexTest
    {
        [TestMethod]
        public void CreateMutexUnderImpersonation()
        {
            var credentials = new NetworkCredential("testagent", "secretpassword");
            var tokenHandle = new IntPtr();
            if (!NativeMethods.LogonUser(credentials.UserName, credentials.Domain, credentials.Password, 2, 0, ref tokenHandle))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            var impersonatedUser = new WindowsIdentity(tokenHandle).Impersonate();
            // this will run indefinately or untill memory is full with 1 cpu core at 100%
            var mutex = new Mutex(true, "test");
        }

        internal static class NativeMethods
        {
            [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
            internal static extern bool LogonUser([MarshalAs(UnmanagedType.LPWStr)]string lpszUsername, [MarshalAs(UnmanagedType.LPWStr)]string lpszDomain, [MarshalAs(UnmanagedType.LPWStr)]string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
        }
    }
}

4
这里是源代码。它展示了CreateMutex在一个while(true)循环内,也许你发现了一个无限循环的错误吗? - Quantic
2
你的 FooForm.FooForm_Load 方法的代码 - giammin
但首先,您确定需要在窗体加载事件中使用互斥锁吗? - giammin
1
你能把完整的代码放在问题里吗(不要只是链接到它)? - EJoshuaS - Stand with Ukraine
1
你应该避免使用 Mutex(bool, string) 来创建命名互斥量。请参见此处 - 使用 Mutex(bool, string, out bool) 代替。你确定需要一个命名的 Mutex 吗? - Bastian Thiede
2个回答

7
    // this will run indefinately or untill memory is full

好的,那可能是一个解释。我们必须假设评论与代码不匹配。最明显的问题在于您没有发布与错误相关的堆栈跟踪,这将无法帮助您诊断潜在问题。我只能发布提示,以便让您进入下一个阶段。
太容易假设它是CreateMutex()失败了。然而,这并非事实,该winapi函数的失败报告方式不同,您会在堆栈跟踪中看到__Error.WinIOError()。错误代码也将不同,您将获得错误1450,ERROR_NO_SYSTEM_RESOURCES,“系统资源不足,无法完成所请求的服务”。
实际上,它是CLR引发的异常。换句话说,是pinvoke marshaller失败了。这使诊断变得相当复杂,在大量的代码中有很多地方可以抛出OOM。通常需要分配非托管内存才能完成pinvoke任务,如果失败,则会出现OOM-kaboom。很多方法可能会发生这种情况,内部非托管堆损坏肯定足够了。尽管您的LogonUser() pinvoke声明在技术上是错误的(CharSet.Auto!= UnmanagedType.LPWStr),但不足以解释此问题。
您需要更接近异常的根源,这需要启用非托管调试器。对于VS2015,请使用Project> Properties> Debugging选项卡>选中“启用本机代码调试”复选框。 CLR需要调试符号才能理解堆栈跟踪。使用Tools> Options> Debugging> Symbols>选中“Microsoft Symbol Server”。您需要使调试器停止在第一次机会异常上,使用Debug> Windows> Exception Settings>选中“Win32 Exceptions”。
现在您将了解更多信息,可以在问题中发布更好的堆栈跟踪。尽管如此,这将给SO用户或您一个清晰的诊断,显示如何通过模拟来解释此故障的可能性很小。寻求Microsoft支持的帮助可能是明智的,但他们需要了解有关“testagent”帐户配置的更多信息。请记住,这些帐户通常被有意地破坏,以确保单元测试无法要求太多的系统资源。

1
你可以使用LOGON32_LOGON_NEW_CREDENTIALS(9)作为LogonType和LOGON32_PROVIDER_WINNT50(3)作为LogonProvider,这将成功。我认为这与 authority 有关。
当你在代码中进行调试时,你会发现它处于无限的 loop 中,mutexHandle = Win32Native.CreateMutex(securityAttribute, initiallyOwned, name); 失败并返回ERROR_ACCESS_DENIED。它将运行 Win32Native.OpenMutex(Win32Native.MUTEX_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);,但也返回ERROR_FILE_NOT_FOUND

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接