Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Winforms app still crashes after unhandled exception handler

I've applied these handlers in a test application:

    Application.ThreadException += Application_ThreadException;
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

I then create an exception on a nested Task/Async await on the form:

Despite the handler being fired - CurrentDomain.UnhandledException - the app still crashes.

Why would it crash and not show the dialog and stay running?

System.Exception was unhandled Message: An unhandled exception of type 'System.Exception' occurred in mscorlib.dll Additional information: dd

private async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("Main " + Thread.CurrentThread.ManagedThreadId);
    try
    {
        await Hello();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception on main  " + Thread.CurrentThread.ManagedThreadId);
    }
}

private async static Task Hello() //changed from void
{
    await Task.Run(() => new IGetRun().Run1());
}

internal class IGetRun
{
    public async Task Run1() //changed from void
    {
        Console.WriteLine("Run1 " + Thread.CurrentThread.ManagedThreadId);
            await Task.Run(() => new IGetRun2().Run2());
    }
}

internal class IGetRun2
{
    public void Run2()
    {
        Console.WriteLine("Run2 " + Thread.CurrentThread.ManagedThreadId);
        throw new Exception("dd");
    }
}

EDIT: Looks like I forgot to declare each async method with Task and not void thus exception handling works predictably now. The only thing I still do not know is why - if I stop handling the exception in the button event - when the exception is caught Application_ThreadException is fired and not TaskScheduler_UnobservedTaskException

like image 316
Jaycee Avatar asked Dec 12 '13 11:12

Jaycee


1 Answers

Your original code does something that you should never do: calling async void methods. And problematic exception handling is one reason for that.

Let's look at the event handlers one by one:

  • Application.ThreadException only handles exceptions on the Winforms UI thread. The exception never got to the UI thread, so this event doesn't fire.
  • TaskScheduler.UnobservedTaskException only handles exceptions from Tasks that are unobserved (and even then, it does so when they are finalized, which might take a long time if you're not allocating much memory). But the unobserved exception is not tied to any Task, so this doesn't fire either.
  • AppDomain.CurrentDomain.UnhandledException handles all exceptions that are still considered unobserved after the previous two event handlers. But it can't be used to set the exception as observed, so the application still terminates. This is the only handler that actually fires.

Why exactly don't the first two handlers fire? In your code, you have Task.Run(() => new IGetRun().Run1()), where Run1() is an async void method that throws an exception.

When there is an unobserved exception in an async void method, the exception is rethrown on the current synchronization context. But since the method is running on a thread pool thread (because of the Task.Run()), there is no synchronization context. So the exception is thrown on a thread pool thread, which is not observable by the first two event handlers.

As you already discovered, the fix for this issue is to use async Task methods and await them properly.


In your updated code, Run1() is now an async Task method. That Task is unwrapped by Task.Run() and then awaited, so the exception is transferred to the Hello() Task. That in turn is awaited from the async void button1_Click handler on the UI thread.

This all means that there are no unobserved Task exceptions and that the exception is rethrown on the UI synchronization context. In Winforms, that means Application.ThreadException is raised, which is exactly what you're observing.

like image 75
svick Avatar answered Oct 20 '22 21:10

svick