By Sam


2014-03-13 04:37:07 8 Comments

This code throws an exception. Is it possible to define an application global handler that will catch it?

string x = await DoSomethingAsync();

Using .net 4.5 / WPF

5 comments

@noseratio 2014-03-14 02:41:44

This is actually a good question, if I understood it correctly. I initially voted to close it, but now retracted my vote.

It is important to understand how an exception thrown inside an async Task method gets propagated outside it. The most important thing is that such exception needs to be observed by the code which handles the completion of the task.

For example, here is a simple WPF app, I'm on NET 4.5.1:

using System;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication_22369179
{
    public partial class MainWindow : Window
    {
        Task _task;

        public MainWindow()
        {
            InitializeComponent();

            AppDomain.CurrentDomain.UnhandledException +=
                CurrentDomain_UnhandledException;
            TaskScheduler.UnobservedTaskException +=
                TaskScheduler_UnobservedTaskException;

            _task = DoAsync();
        }

        async Task DoAsync()
        {
            await Task.Delay(1000);

            MessageBox.Show("Before throwing...");

            GCAsync(); // fire-and-forget the GC

            throw new ApplicationException("Surprise");
        }

        async void GCAsync()
        {
            await Task.Delay(1000);

            MessageBox.Show("Before GC...");

            // garbage-collect the task without observing its exception 
            _task = null;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        }

        void TaskScheduler_UnobservedTaskException(object sender,
            UnobservedTaskExceptionEventArgs e)
        {
            MessageBox.Show("TaskScheduler_UnobservedTaskException:" +
                e.Exception.Message);
        }

        void CurrentDomain_UnhandledException(object sender,
            UnhandledExceptionEventArgs e)
        {
            MessageBox.Show("CurrentDomain_UnhandledException:" +
                ((Exception)e.ExceptionObject).Message);
        }
    }
}

Once ApplicationException has been thrown, it goes unobserved. Neither TaskScheduler_UnobservedTaskException nor CurrentDomain_UnhandledException gets invoked. The exception remains dormant until the _task object gets waited or awaited. In the above example it never gets observed, so TaskScheduler_UnobservedTaskException will be invoked only when the task gets garbage-collected. Then this exception will be swallowed.

The old .NET 4.0 behavior, where the AppDomain.CurrentDomain.UnhandledException event gets fired and the app crashes, can be enabled by configuring ThrowUnobservedTaskExceptions in app.config:

<configuration>
    <runtime>
      <ThrowUnobservedTaskExceptions enabled="true"/>
    </runtime>
</configuration>

When enabled this way, AppDomain.CurrentDomain.UnhandledException will still be fired after TaskScheduler.UnobservedTaskException when the exception gets garbage-collected, rather than on the spot where it thrown.

This behavior is described by Stephen Toub in his "Task Exception Handling in .NET 4.5" blog post. The part about task garbage-collection is described in the comments to the post.

That's the case with async Task methods. The story is quite different for async void methods, which are typically used for event handlers. Let's change the code this way:

public MainWindow()
{
    InitializeComponent();

    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

    this.Loaded += MainWindow_Loaded;
}

async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    await Task.Delay(1000);

    MessageBox.Show("Before throwing...");

    throw new ApplicationException("Surprise");
}

Because it's async void there's no Task reference to hold on to (so there's nothing to be possibly observed or garbage-collected later). In this case, the exception is thrown immediately on the current synchronization context. For a WPF app, Dispatcher.UnhandledException will be fired first, then Application.Current.DispatcherUnhandledException, then AppDomain.CurrentDomain.UnhandledException. Finally, if none of these events are handled (EventArgs.Handled is not set to true), the app will crash, regardless of the ThrowUnobservedTaskExceptions setting. TaskScheduler.UnobservedTaskException is not getting fired in this case, for the same reason: there is no Task.

@Sam 2014-03-14 18:15:23

Noseratio you are spot on in understanding my question. However The fact that UnobservedTaskException is not raised until the GC is run makes it functionally useless. Toub provides the IgnoreExceptions solution in the questions section of his post. I dont understand why the compiler does not add this when it creates the async method wrapper. Inside the continuation it should raise UnobservedTaskExpception if the event is handled.

@noseratio 2014-03-14 22:06:32

@Sam, IMO this behavior makes perfect sense. A Task object is a promise, a delayed result. The fact that it has just completed doesn't mean it has to be observed right away. It may be observed in 10 minutes from now, as a result of composition with other tasks ( e.g. with WhenAll), or other specifics of your asynchronous logic. Why should the compiler make any assumptions about this and break such logic? It should not.

@noseratio 2014-03-14 22:07:12

You have complete control over this. Either handle the exception on the spot where it may occur with try/catch, or use something like the aforementioned IgnoreException to mark it as observed.

@Sam 2014-03-14 23:01:50

Thank you Noseratio, what I am saying is that I need a way to create an "observer of last resort" or global handler. If I handle DispatcherUnhandledException I am not asking the compiler to make an assumption - I am giving it a specific instruction. If the compiler adds Toubs IgnoreExceptions or something similar it can fire DispatcherUnhandledException if it I handle it.

@Sam 2014-03-14 23:03:07

BTW look at the answer provided by Todd Menier. Although his answer is wrong, his reasoning is correct. Namely - I can define a global handler for a non-async application, why cant I define one for an async app?

@noseratio 2014-03-14 23:09:28

@Sam, I disagree with that but I don't know how to explain it better than I already did. This behavior is dictated by the asynchronous nature of Task. As I said, it may get observed now, or it may get observed in 10 minutes from now, still legitimately. I don't want the compiler or DispatcherUnhandledException get in the way meanwhile.

@Vince Panuccio 2014-05-09 11:15:22

I've been playing around with this code and have discovered that the AppDomain FirstChanceException is always thrown in all cases where the task is not observed. pastebin.com/wfqP3XSh

@noseratio 2014-05-09 11:18:56

@VincePanuccio, that's true and expected, but I don't think it's useful. This event will be fired for any simple case like this: try { throw new Exception(); } catch(e) { /* handle */ }, before the catch block has a chance to handle the error.

@Vince Panuccio 2014-05-09 11:23:33

@Noseratio In your question you asked for a global exception handler and this one indeed is global. So you want a global exception handler that fires only if the surrounding code is not handled by a try catch block or always fires after a catch block has dealt with it? :-)

@noseratio 2014-05-09 11:26:36

@VincePanuccio, I'm not the author of the question, the OP is @Sam :) If you think AppDomain.FirstChanceException would suit him better, you should post your own answer here.

@Vince Panuccio 2014-05-09 11:33:14

@Noseratio Oops! My mistake :)

@Vince Panuccio 2014-05-09 12:04:47

@Noseratio If I throw an exception after GC.Collect then the UnhandlledException event is fired even though the Task no longer exists. The fire-and-forget method GCAsync() is not being observed. Can you explain this behavior ?

@noseratio 2014-05-09 13:09:12

@VincePanuccio, I need to see and try the actual code. Can you post it as a separate question?

@Hossein Shahdoost 2016-09-25 14:04:48

You can always do the following to handle the exception using Application.DispatcherUnhandledException method. Of course it would be given to you inside a TargetInvocationException and might not be as pretty as other methods. But it works perfectly fine

_executeTask = executeMethod(parameter);
_executeTask.ContinueWith(x =>
{
    Dispatcher.CurrentDispatcher.Invoke(new Action<Task>((task) =>
    {
        if (task.Exception != null)
           throw task.Exception.Flatten().InnerException;
    }), x);
}, TaskContinuationOptions.OnlyOnFaulted);

@Vince Panuccio 2014-05-09 11:40:56

Binding an event to the AppDomain.CurrentDomain.FirstChanceException will guarantee you that your exception will be caught. As @Noseratio pointed out, you'll be notified of every exception in your application, even if the exception is handled gracefully within a catch block and the application continues on.

However, I still see this event being useful for at least capturing the last few exceptions thrown before an application halted or perhaps some other debugging scenario.

If you want to protect yourself against this

string x = await DoSomethingAsync();

My advice to you is, don't do that, add a try catch block :-)

@Ned Stoyanov 2014-03-13 06:59:44

EDITED as per @Noseration's comment

In .NET 4.5 in async code you can handle unobserved exceptions by registering a handler for the TaskScheduler.UnobservedTaskException event. An exception is deemed unobserved if you do not access the Task.Result, Task.Exception properties and you do not call Task.Wait.

After the unobserved exception reaches the TaskScheduler.UnobservedTaskException event handler, the default behaviour is to swallow this exception so the program does not crash. This behaviour can be changed in the configuration file by adding the following:

<configuration> 
   <runtime> 
      <ThrowUnobservedTaskExceptions enabled="true"/> 
   </runtime> 
</configuration>

@Todd Menier 2014-03-14 02:29:43

True, but in this case he is awaiting the result so the task will be considered observed.

@noseratio 2014-03-14 03:38:23

This was true for .NET 4.0, but not anymore for .NET 4.5+. Check my answer for details.

@Todd Menier 2014-03-14 02:27:35

Well, how would you define an application global handler to deal with an exception in this case?

string x = DoSomething();

Chances are the answer to your question is exactly the same. It appears you are properly awaiting an async method, and the compiler goes to great lengths to ensure that any exception that occurs in the async method is propagated and unwound in a way that allows you to handle it just like you would in synchronous code. This is one of the primary benefits of async/await.

Related Questions

Sponsored Content

17 Answered Questions

[SOLVED] Why catch and rethrow an exception in C#?

7 Answered Questions

[SOLVED] Manually raising (throwing) an exception in Python

  • 2010-01-12 21:07:40
  • TIMEX
  • 1617422 View
  • 2091 Score
  • 7 Answer
  • Tags:   python exception

5 Answered Questions

[SOLVED] Catch multiple exceptions in one line (except block)

32 Answered Questions

[SOLVED] The case against checked exceptions

33 Answered Questions

[SOLVED] How do you assert that a certain exception is thrown in JUnit 4 tests?

36 Answered Questions

[SOLVED] What is the correct way to create a single-instance WPF application?

  • 2008-08-21 00:33:59
  • Nidonocu
  • 190046 View
  • 631 Score
  • 36 Answer
  • Tags:   c# .net wpf mutex

28 Answered Questions

[SOLVED] Catch multiple exceptions at once?

6 Answered Questions

4 Answered Questions

[SOLVED] .NET Global exception handler in console application

Sponsored Content