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

Per*_*nce 5 .net c# pinvoke winapi garbage-collection

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

应用程序崩溃并显示“ .NET运行时内部错误”

完整的错误是:

由于.NET运行时在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;
        }
Run Code Online (Sandbox Code Playgroud)

其中FindWindowEx()EnumChildWindows()并且GetWindowText()都的P / Invoke签名定义类似于此:

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

在我多次运行该方法后,似乎只会出现该错误,但是这种情况并不会一直发生。有时它起作用,有时却不起作用。

对于如何解决这个问题,有任何的建议吗?

Per*_*nce 5

因此,我在Discord的慷慨捐助者的帮助下解决了我的问题。

问题是我将a Lamda作为代理传递给ap / invoke:

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

因此,每次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;
        }
Run Code Online (Sandbox Code Playgroud)

该应用程序现在可以按预期工作。

  • 该代码不存在于问题中。就目前而言,任何人都无法按照书面回答该问题。如果您希望继续解决该问题,则必须对其进行编辑,以便可以在问题中看到缺陷。如果您这样做,我们可以将其关闭。我个人会删除答案和问题,因为对未来的访问者来说价值不大。 (5认同)
  • 我们在这里有一个问答模型。问题需要描述问题。用答案描述问题是没有用的。那是回到前面。 (3认同)
  • 因此,通过将 lamda 传递给本机函数并很好地解释为什么会导致该错误,这没有什么价值? (2认同)
  • 问题是错误......解释错误的原因和解决方案是非常有用的信息。我将编辑问题以在问题中而不是在答案中提取问题代码,但我真的不明白为什么只要信息存在就“从后到前”这很重要。 (2认同)
  • 这就是我们在这里做的事情。问题和答案。贴心通知解释。 (2认同)