开发者

Is it possible to fail triggering the AppDomain's unhandled exception handler from a Task continuation?

开发者 https://www.devze.com 2023-04-04 09:49 出处:网络
I have a Task which runs asynchronously and handles exceptions using a task continuation task.ContinueWith(t => ..., CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, taskScheduler).

I have a Task which runs asynchronously and handles exceptions using a task continuation task.ContinueWith(t => ..., CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, taskScheduler).

This works great for handling exceptions I know how to handle (e.g. WebException), but what if something like NullReferenceException is thrown, which I can't handle correctly?

I want to be able to handle it using the AppDomain's unhandled exception handler (which in our case logs the error, pops up a dialog notifying the user, and exits the application), but so far I have not found a good way to do this.

Simply rethrowing the exception from the continuation results in the unhandled exception handler being called when the task is finalized (because the rethrown exception is unobserved). This is not good because it may be seconds or even minutes after I know the application should crash.

Calling Environment.FailFast() does crash the application but does not call the unhandled exception handler.

Calling Thread.CurrentThread.Abort() calls the unhandled exception handler but only displays information/the stack trace from the ThreadAbortException. Plus it just seems like a bad idea to use this method.

Calling Application.OnThreadException() actually does exactly what I want, except that it requires referencing System.Windows.Forms and handling Application.ThreadException which will not work with, for example, a service with no UI.

Calling task.Wait() does not make sense because this in our case there's no place to call it from. If the task succeeds the result is processed using a success continuation, if it fails the exception continuation is called, and we can't block the thread creating the task (that's the whole point of the task).

I could call the unhandled exception handler directly except that in at least one case that would require adding a dependenc开发者_开发技巧y I don't want to add in my application.

Am I missing an obvious way to do this?


I didn't find any BCL class which does similar work. So, I can suggest you to use some hand-written static class:

static class Crusher
{
    private static readonly AutoResetEvent CrushEvent;
    private static Exception _exception;

    static Crusher()
    {
        CrushEvent = new AutoResetEvent(false);
    }

    public static Thread GetCrushWaitingThread()
    {
        return new Thread(WaitForCrush);
    }

    static void WaitForCrush()
    {
        CrushEvent.WaitOne();
        throw _exception;
    }

    public static void Crush(Exception exception)
    {
        _exception = exception;
        CrushEvent.Set();
    }
}

You should just init it at your application startup:

static void Main(string[] args)
{
    AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
    Crusher.GetCrushWaitingThread().Start();
    ...

and then - you can call it from continuations:

task.ContinueWith(Continuation, TaskContinuationOptions.OnlyOnFaulted);

...

private static void Continuation(Task obj)
{
    Crusher.Crush(obj.Exception);
}

But it's not as nice as real rethrown unhandled exception (it has addition information about Crusher.WaitForCrush() method in stacktrace).


We used Application.OnThreadException() to solve our specific case.


Just use the same logic that you implemented for AppDomain.UnhandledException and call it explicitly when you hit an exception in your continuation. Here is an example crash handler I use:

public static class CrashHandler
{
    public static void Setup()
    {
        AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
    }

    public static Task CrashOnUnhandledException(this Task t)
    {
        t.ContinueWith(x => HandleException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
        return t;
    }

    private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        HandleException(e.ExceptionObject);
    }

    private static void HandleException(object ex)
    {
        ShowErrorMessage(ex);

        Environment.Exit(-1);
    }

    private static void ShowErrorMessage(object ex)
    {
        // your crash dialog goes here
    }
}

The extension method CrashOnUnhandledException can be used like this to save you some typing and give you additional information about the call site in your stack trace:

task.ContinueWith(t => { /* your logic */ })
    .CrashOnUnhandledException();
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号