.NET 运行时错误 80131506 - 将 Lambda 传递给本机函数

.NET Runtime Error 80131506 - Passing Lambda to Native Function

所以我收到这个错误,看起来好像是损坏的垃圾收集:

Application Crashes With "Internal Error In The .NET Runtime"

完整的错误是:

The process was terminated due to an internal error in the .NET Runtime at IP 71C571C8 (71B20000) with exit code 80131506.

运行时间:

Framework Version: v4.0.30319

当运行重复使用这个函数时会出现不一致的情况:

public static int GetMdiTitledChildWindows(IntPtr parentWindow)
        {
            IntPtr mdiClient = FindWindowEx(parentWindow, IntPtr.Zero, MdiClient, "");
            List<IntPtr> handles = new List<IntPtr>();
            EnumChildWindows(mdiClient, (hwnd, param) =>
            {
                handles.Add(hwnd);
                return true;
            }, IntPtr.Zero);
            int counter = 0;
            foreach (IntPtr handle in handles)
            {
                StringBuilder builder = new StringBuilder();
                GetWindowText(handle, builder, GetWindowTextLength(handle)+1);
                if (builder.Length > 0)
                {
                    counter++;
                }
            }
            return counter;
        }

其中 FindWindowEx()EnumChildWindows()GetWindowText() 都是 p/invoke 签名,其定义与此类似:

[DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

这个错误似乎只在我 运行 多次使用该方法后才会出现,但是,这种情况不会一直发生。有时有效,有时无效。

关于如何解决这个问题有什么建议吗?

所以我在 Discord 上一位慷慨的捐助者的帮助下解决了我的问题。

问题是我将 Lamda 作为委托传递给 p/invoke:

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

所以每次 unmanaged WinAPI 调用回调到我的委托时,GC 都有机会 运行,如果有的话,它收集了我的委托lamda 导致这次崩溃。这不一定会发生,这就是为什么我的方法大部分时间都有效并且不一致地崩溃的原因。

解决方案是添加对 lamda 的引用,以防止 GC 收集它(尽管我全力以赴并将其设为本地函数,因为皮带和大括号):

public static int GetMdiTitledChildWindows(IntPtr parentWindow)
        {
            IntPtr mdiClient = FindWindowEx(parentWindow, IntPtr.Zero, MdiClient, "");
            List<IntPtr> handles = new List<IntPtr>();
            bool addToList(IntPtr hwnd, IntPtr param)
            {
                handles.Add(hwnd);
                return true;
            }
            EnumWindowsProc gcHolder = addToList;
            EnumChildWindows(mdiClient, gcHolder, IntPtr.Zero);
            int counter = 0;
            foreach (IntPtr handle in handles)
            {
                int textLength = GetWindowTextLength(handle) + 1;
                StringBuilder builder = new StringBuilder(textLength);
                GetWindowText(handle, builder, textLength);
                if (builder.Length > 0)
                {
                    counter++;
                }
            }
            return counter;
        }

该应用程序现在按预期运行。