AppDomain.FirstChanceException event subscription causing FatalExecutionEngineError

1

This class exists in its own DLL. Calling the Ex method fairly obviously causes an exception to be thrown and handled:

class Foo : MarshalByRefObject
{
    public void Ex()
    {
        object o = null;

        try
        {
            string s = o.ToString();
        }
        catch { }
    }
}

The following loads the DLL containing Foo into another AppDomain, creates a Foo and calls the Ex method.

using System;
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using RemoteCode;

namespace AppDomainTraceTest
{
    class Program
    {
        static void Main()
        {
            AppDomain appDomain = AppDomain.CreateDomain("TEST",
                null,
                new AppDomainSetup
                {
                    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
                });

            // appDomain.FirstChanceException +=
            //     (s, e) => Debug.WriteLine("* " + e.Exception.Message);

            Bar bar = new Bar();
            bar.Subscribe(appDomain);

            Foo foo = (Foo)appDomain.CreateInstance("RemoteCode", "RemoteCode.Foo").Unwrap();
            foo.Ex();
        }
    }

    public class Bar : MarshalByRefObject
    {
        public void Subscribe(AppDomain ad)
        {
            ad.FirstChanceException += OnFirstChanceException;
        }

        private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e)
        {
            Debug.WriteLine("* " + e.Exception.Message);
        }
    }
}

The result is a FatalExecutionEngineError:

The runtime has encountered a fatal error. The address of the error was at 0x62c6529d, on thread 0x26f0. The error code is 0x80131506. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

It occurs on this line in Foo:

string s = o.ToString();

If I comment out the lines including Bar, and uncomment the preceding line then the code works fine and the exception message is written to the debug console:

appDomain.FirstChanceException +=
    (s, e) => Debug.WriteLine("* " + e.Exception.Message);

//Bar bar = new Bar();
//bar.Subscribe(appDomain);

Foo foo = (Foo)appDomain.CreateInstance("RemoteCode", "RemoteCode.Foo").Unwrap();
foo.Ex();

Am I doing something stupid, or is this a bug?

c#
.net
asked on Stack Overflow Oct 9, 2013 by (unknown user)

1 Answer

1

The label "bug" is pretty appropriate in a case like this, there should never be a FEEE from plain managed code. You are however giving the CLR an awfully hard time, marshaling the event handler call from one AppDomain to another at a very critical time. Clearly it is not equipped to deal with that.

You'll need to strongly consider avoiding this. It works just fine as long as you keep the event handler inside the same AppDomain. This works fine for example:

public class Foo : MarshalByRefObject {
    public Foo() {
        AppDomain.CurrentDomain.FirstChanceException += (s, e) => {
            Debug.WriteLine("* " + e.Exception.Message);
        };
    }
    // etc...
}

Probably inconvenient for logging I'd imagine. You can file a feedback report for this bug at connect.microsoft.com

answered on Stack Overflow Oct 9, 2013 by Hans Passant • edited Oct 10, 2013 by Hans Passant

User contributions licensed under CC BY-SA 3.0