6

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?

OrangeDog
  • 33,501
  • 12
  • 115
  • 195
Persistence
  • 3,136
  • 3
  • 27
  • 66
  • Isn't this a duplicate of the question you've linked? – OrangeDog Sep 24 '18 at 11:05
  • 2
    @orangedog I don't believe so because as far as I can tell the issue in the question I've linked was caused by concurrent garbage collection not by passing a lamda – Persistence Sep 24 '18 at 11:07

1 Answers1

7

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

The problem was that I was passing a Lamda 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 lamda 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 lamda 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.

Persistence
  • 3,136
  • 3
  • 27
  • 66
  • 5
    This code was not present in the question. As it stands, there is no way for anybody to answer the question as written. If you wish to persist with the question then you must edit it so that the defect can be seen in the question. If you do that we can close it as a duplicate. Personally I'd remove both answer and question because there is little value to future visitors. – David Heffernan Sep 17 '18 at 07:43
  • 2
    So there's little value in showing that that error can be caused by passing a lamda to a native function with a good explanation of why? – Persistence Sep 17 '18 at 16:55
  • 3
    We have a question and answer model here. The question needs to describe the problem. It's no good to describe the problem in an answer. That's back to front. – David Heffernan Sep 17 '18 at 17:24
  • 2
    The problem is the error... Explaining the cause of the error and the solution is pretty clearly useful information. I'll edit the question to have the extract of the problem code in the question rather than in the answer but I really don't see why it matters if its "back to front" as long as the information is there. – Persistence Sep 17 '18 at 17:36
  • 2
    This is just how we do things here. Question and answer. Explained by the close notice. – David Heffernan Sep 17 '18 at 18:16
  • Edit the question as you describe and I will vote to reopen. LMK – Paul Sanders Sep 21 '18 at 22:28
  • @DavidHeffernan that better? – Persistence Sep 22 '18 at 23:09
  • Indeed it is. Voted up (SO can be a cruel place) and to reopen. I'd almost call that a bug in .Net, perhaps you should report it. – Paul Sanders Sep 23 '18 at 14:27