Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UnhandledException not called when exception thrown in another thread

According to the Microsoft documentation, when an unhandled exception occurs on a thread (from either the thread pool or created using the System.Threading.Thread class) the AppDomain.UnhandledException event should fire for the default AppDomain of the application. Here is the MSDN link which explains it after the second NOTE section.

But I cannot reproduce this behaviour, as far as I can tell from my test application it never fires the UnhandledException on either the default AppDomain or the AppDomain used to create the thread. Is the documentation wrong or my testing code?

using System;
using System.Runtime.ExceptionServices;
using System.Reflection;

public class Program
{
    static void Main()
    {
        Program.HookAppDomainExceptions();
        Test t = CreateTestInsideAppDomain("Nested1");
        t.SetupNested1();
        Console.ReadLine();
    }

    public static Test CreateTestInsideAppDomain(string appDomainName)
    {
        AppDomain nested1 = AppDomain.CreateDomain(appDomainName);
        string executingName = Assembly.GetExecutingAssembly().FullName;
        return (Test)nested1.CreateInstanceAndUnwrap(executingName, "Test");
    }

    public static void HookAppDomainExceptions()
    {
        AppDomain.CurrentDomain.FirstChanceException +=
            new EventHandler<FirstChanceExceptionEventArgs>(FirstChanceException);

        AppDomain.CurrentDomain.UnhandledException +=
            new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
    }

    public static void FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
    {
        Console.WriteLine("Domain:{0} FirstChanceException Handler",
                          AppDomain.CurrentDomain.FriendlyName);
    }

    public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("Domain:{0} UnhandledException Handler",
                          AppDomain.CurrentDomain.FriendlyName);
    }
}

public class Test : MarshalByRefObject
{
    private delegate void Nothing();

    public void SetupNested1()
    {
        var start = new Nothing(Nested1ThreadStart);
        start.BeginInvoke(null, null);
    }

    static void Nested1ThreadStart()
    {
        Program.HookAppDomainExceptions();
        Test t = Program.CreateTestInsideAppDomain("Nested2");
        t.SetupNested2();
    }

    public void SetupNested2()
    {
        Program.HookAppDomainExceptions();
        Test t = Program.CreateTestInsideAppDomain("Nested3");
        t.ThrowException();
    }

    public void ThrowException()
    {
        Program.HookAppDomainExceptions();
        throw new ApplicationException("Raise Exception");
    }
}
like image 396
Phil Wright Avatar asked Sep 11 '11 14:09

Phil Wright


1 Answers

In your code UnhandledException isn't fired on any AppDomain, because if you call a delegate using BeginInvoke(), any exception that is thrown during its execution is handled and then rethrown when you call EndInvoke(), which you don't.

If you either call EndInvoke():

start.EndInvoke(start.BeginInvoke(null, null));

or execute the delegate synchronously:

start();

You get similar results: UnhandledException of the main domain is raised.

If instead, you do what the documentation says and start a new thread using the Thread class:

new Thread(Nested1ThreadStart).Start();

UnhandledException of Nested1 and the main app domain are raised.

So, to answer your question: The documentation is right. Your code is wrong. When you call delegate asynchronously using BeginInvoke(), you should always call EndInvoke() later.

like image 84
svick Avatar answered Oct 19 '22 03:10

svick