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

7

我遇到了一个错误,看起来像是垃圾回收出现了问题:

应用程序崩溃,并显示“在 .NET Runtime 中发生内部错误”

完整的错误信息如下:

进程由于 .NET Runtime 内部错误(IP 71C571C8 (71B20000),退出代码为 80131506)而终止。

正在运行的环境为:

框架版本: 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);

这个错误似乎只在我多次运行该方法后出现,但这种情况并不一致。有时它可以工作,有时它不能。

有什么建议可以解决这个问题吗?


这不是你链接的那个问题的重复吗? - OrangeDog
2
@orangedog 我不这么认为,因为据我所知,我链接的问题是由并发垃圾回收引起的,而不是通过传递一个 lambda 引起的。 - ScottishTapWater
1个回答

9

我通过Discord上一位慷慨的资助者解决了我的问题。

问题是我将一个lambda表达式作为委托传递给了p/invoke:

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

每次调用未托管WinAPI回调我的委托时,GC都有机会运行。如果它运行了,就会收集我的lambda,导致崩溃。这不一定会发生,因此我的方法大部分时间都能正常工作,但也不稳定。
解决方案是添加对lambda的引用,以防止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;
        }

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

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