2

My application has global exception handlers (ok, loggers, I know they don't technically "handle" them) which are simply something like:

public static class UnhandledExceptionHandler
{
    public static void Init()
    {
        AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainOnUnhandledException;

        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        Application.ThreadException += ApplicationOnThreadException;
    }

    private static void ApplicationOnThreadException(object sender, ThreadExceptionEventArgs e)
    {
        if (e.Exception != null)
            MessageBox.Show(e.Exception.ToString());
    }

    private static void OnCurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        var ex = e.ExceptionObject as Exception;

        if (ex != null)
            MessageBox.Show(ex.ToString());
    }
}

In Main() is

UnhandledExceptionHandler.Init();

But I am finding that when unhandled exceptions occur within a Task.ContinueWith these events are not raised. Visual Studio recognises them as unhandled as they are highlighted in the debugger.

For example, you can test this with simply:

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(() => { })
    .ContinueWith(t =>
    {
        throw new Exception("continue with exception");
    });
}

You will notice that no MessageBox gets raised (nor are either of the events raised).

Just to prove it does work, the following will work:

private void button2_Click(object sender, EventArgs e)
{
    throw new Exception("click failed");
}

Anyone have any ideas why and/or how I might be able to catch and log these unhandled exceptions?

Of course, I appreciate that it would probably make sense to keep the amount of logic inside of the ContinueWith to a minimum, but it may still occur.

This is currently .NET 4.0

oatsoda
  • 2,088
  • 2
  • 26
  • 49
  • `Task.Factory.StartNew/ContinueWith` returns a Task which has a *Exception* property. – L.B Feb 19 '14 at 11:34
  • Ok, but would that not mean in my example above I would then have to have **another** ContinueWith which I then check `t.Exception`? Would I then need an infinite number of ContinueWith's just in case :) – oatsoda Feb 19 '14 at 11:41
  • Writing an infinite number of ContinueWiths only makes sense if you take your user name too seriously. Using the overload that takes TaskContinuationOptions.OnlyOnFaulted tends to be useful. Do avoid writing fire-and-forget code. – Hans Passant Feb 19 '14 at 11:47
  • It's a shame though that I have to append a `ContinueWith` with OnlyOnFaulted to a `Task` **after** any other `ContinueWith`s in order to ensure I handle exceptions. – oatsoda Feb 19 '14 at 11:52

1 Answers1

1

From the comments:

It's a shame though that I have to append a ContinueWith with OnlyOnFaulted to a Task after any other ContinueWiths in order to ensure I handle exceptions.

You don't have to. Just use the Then pattern by Stephen Toub from "Processing Sequences of Asynchronous Operations with Tasks". Also, make sure to read "Tasks and Unhandled Exceptions", it will help to better understand the behavior you're seeing.

That said, you may want to seriously consider using Microsoft.Bcl.Async and async/await, if you code with VS2012+. This will make you forget about ContinueWith, and use natural try/catch for handling exceptions.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    Whilst this doesn't answer my question, this really helps to understand the way that `ContinueWith` returns a **new*** `Task` which when combined with `OnlyOnFaulted` will only execute upon failure. Therefore chaining these `ContinueWith`s do not behave as expected. – oatsoda Feb 28 '14 at 13:50