Unhandled exception handler not called for Metro / WinRT UI async void event handler

主宰稳场 提交于 2019-12-28 13:22:15

问题


Consider the following to be extracts from a Windows 8 Metro / WinRT app, which have been reduced to the bare minimum required to show the anomaly:

public class App : Application
{
    public App()
    {
        UnhandledException += (sender, e) => e.Handled = true;
    }
}

public class MainPage : Page
{
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        throw new NotSupportedException();
    }

    private async void Button_Click_2(object sender, RoutedEventArgs e)
    {
        throw new NotSupportedException();
    }
}

So given a Metro UI with two buttons and their click event handlers, the only difference is that the second event handler is marked as async.

Then clicking on each button, I would expect the UnhandledException handler to be called in both cases, since they (should) both be entered via the UI thread and associated synchronization context. My understanding is that, for async void methods, any exceptions should be captured and 'rethrown' (preserving the original stacktrace) via the initial synchronization context, which is also clearly stated in the Async / Await FAQ.

But the UnhandledException handler is not called in the async case, so the application crashes! Since this challenges what I consider an otherwise very intuitive model, I need to know why! Yes, I know I could wrap the body of the handler in a try { } catch { }, but my question is why isn't the backstop UnhandledException handler called?

To further emphasise why this doesn't make sense, consider the following practically identical extracts from a WPF app also using async / await and targeting .NET Framework 4.5:

public class App : Application
{
    public App()
    {
        DispatcherUnhandledException += (sender, e) => e.Handled = true;
    }
}

public class MainWindow : Window
{
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        throw new NotSupportedException();
    }

    private async void Button_Click_2(object sender, RoutedEventArgs e)
    {
        throw new NotSupportedException();
    }
}

[There is a subtle difference that WPF has both an Application DispatcherUnhandledException event handler as well as an AppDomain UnhandledException event handler, but you can only mark the exception as 'handled' in the DispatcherUnhandledException, which aligns with the Metro / WinRT Application UnhandledException event handler above.]

Then clicking on each button, the DispatcherUnhandledException handler is indeed called in both cases, as expected, and the application does not crash.


回答1:


Answered here: No UnhandledException fired from async event callback

It is a known limitation of WinRT. Hopefully, it gets fixed in the next update.




回答2:


The solution in the following post worked for me, with one small change: I had to move AsyncSynchronizationContext.Register(); to the App.OnLaunched event

http://www.markermetro.com/2013/01/technical/handling-unhandled-exceptions-with-asyncawait-on-windows-8-and-windows-phone-8/




回答3:


As explained in the documentation (source: http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx):

It’s important to be aware of several limitations of the Application.UnhandledException event. This event is only used with exceptions encountered by the XAML framework. Exceptions encountered by other Windows Runtime components or parts of the application that are not connected to the XAML framework will not result in this event being raised.

For example, if a different Windows component calls into application code and an exception is thrown and not caught, the UnhandledException event won’t be raised. If the application creates a worker thread, and then raises an exception on the worker thread, the UnhandledException event won’t be raised.

As pointed out in this conversation, only way to retrieve exceptions happening in a worker thread is to wrap them in a try/catch block. As a consequence, here's the workaround I'm using in my app: instead of using Task.Run or equivalents to execute code on a worker thread from the UI, I'm using this method:

/// <summary>
/// Runs code in a worker thread and retrieves a related exception if any.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="action">The action.</param>
public static void SafeRun(this DependencyObject target, Action action)
{
    Task task = ThreadPool.RunAsync(o =>
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            /* Call the same error logging logic as in the UnhandledException event handler here. */
        }
    }).AsTask();
    task.Wait();
}

Bottom line is that to log all errors in your app, you should:

  1. Subscribre to the UnhandledException event to get XAML stack related errors,
  2. Wrap actions happening in a worker threads in a try/catch block.



回答4:


From my point of view the only right answere was downvoted here (Try UnobservedTaskException event of TaskScheduler)

the problem is that you are using 'async void' where the exceptions cannot be handled. This is not a WinRT limitation but as design behaviour of async. You need to understand the async deeply to correctly implement exception handling here. See this article: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Exceptions are rethrown whe GC collects the tasks as unobserved. You can get them by registering TaskScheduler UnobservedTaskException event. Note - it takes some time till the exception arrives there, because it is noted with garbage collector.

Generally do not use 'async void', but in UI event handlers you have to, so this is thy only way..




回答5:


Try UnobservedTaskException event of TaskScheduler



来源:https://stackoverflow.com/questions/12344357/unhandled-exception-handler-not-called-for-metro-winrt-ui-async-void-event-han

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!