Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Runtime Error 80131506 - Passing Lambda to Native Function

So I'm getting this error that looks as though it's a corrupted garbage collection:

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

The full error is:

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

It's running on:

Framework Version: v4.0.30319

This occurs inconsistently when running this function repeatedly:

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

Where FindWindowEx(), EnumChildWindows() and GetWindowText() are all p/invoke signatures defined similarly to this:

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

The error only seems to occur after I've run the method many times, however, this doesn't happen consistently. Sometimes it works, sometimes it doesn't.

Any suggestions on how to fix this?

like image 201
Persistence Avatar asked Sep 17 '18 02:09

Persistence


1 Answers

So I solved my issue with the help of a generous benefactor on Discord.

The problem was that I was passing a lambda to a p/invoke as a delegate:

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

So every time the unmanaged WinAPI call called-back into my delegate, the GC had the opportunity to run, if it did, it collected my lambda causing this crash. This wouldn't necessarily happen, hence why my method worked most of the time and crashed inconsistently.

The solution was to add a reference to the lambda that would prevent the GC from collecting it (although I went whole-hog and made it a local function because belt and braces):

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

The application now works as expected.

like image 98
Persistence Avatar answered Oct 28 '22 16:10

Persistence