13

将以下内容视为从 Windows 8 Metro / WinRT 应用程序中提取的内容,这些内容已减少到显示异常所需的最低限度:

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();
    }
}

因此,给定一个带有两个按钮及其 click 事件处理程序的 Metro UI,唯一的区别是第二个事件处理程序被标记为async.

然后单击每个按钮,我希望在这两种情况下都会调用 UnhandledException 处理程序,因为它们(应该)都通过 UI 线程和关联的同步上下文输入。我的理解是,对于async void方法,任何异常都应该被捕获并通过初始同步上下文“重新抛出”(保留原始堆栈跟踪),这在Async / Await FAQ中也有明确说明。

但是在这种情况下没有调用UnhandledException 处理程序async,所以应用程序崩溃了!由于这挑战了我认为非常直观的模型,我需要知道为什么!是的,我知道我可以将处理程序的主体包装在 a 中try { } catch { },但我的问题是为什么不调用 backstop UnhandledException 处理程序?

为了进一步强调为什么这没有意义,请考虑以下来自 WPF 应用程序的几乎相同的摘录,也使用 async / await 并以 .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();
    }
}

[WPF既有Application DispatcherUnhandledException事件处理程序也有AppDomain UnhandledException事件处理程序的细微差别,但只能在DispatcherUnhandledException中将异常标记为“已处理”,这与Metro/WinRT Application UnhandledException事件处理程序一致以上。]

然后单击每个按钮,DispatcherUnhandledException 处理程序确实在两种情况下都被调用,正如预期的那样,应用程序不会崩溃

4

6 回答 6

5

在这里回答:异步事件回调没有触发 UnhandledException

这是 WinRT 的一个已知限制。希望它在下一次更新中得到修复。

于 2012-10-08T16:15:14.057 回答
5

以下帖子中的解决方案对我有用,但有一个小改动:我不得不移动 AsyncSynchronizationContext.Register(); 到 App.OnLaunched 事件

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

于 2013-09-05T02:33:24.053 回答
4

如文档中所述(来源:http: //msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx):

了解 Application.UnhandledException 事件的几个限制非常重要。此事件仅用于 XAML 框架遇到的异常。未连接到 XAML 框架的其他 Windows 运行时组件或应用程序部分遇到的异常不会导致引发此事件。

例如,如果一个不同的 Windows 组件调用了应用程序代码并且抛出了一个异常但未被捕获,则不会引发 UnhandledException 事件。如果应用程序创建一个工作线程,然后在该工作线程上引发异常,则不会引发 UnhandledException 事件。

正如此对话中所指出的,检索工作线程中发生的异常的唯一方法是将它们包装在 try/catch 块中。因此,这是我在我的应用程序中使用的解决方法:我没有使用Task.Run或等效项在 UI 的工作线程上执行代码,而是使用此方法:

/// <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();
}

底线是要记录应用程序中的所有错误,您应该:

  1. 订阅UnhandledException事件以获取 XAML 堆栈相关的错误,
  2. 将工作线程中发生的操作包装在 try/catch 块中。
于 2012-12-07T08:56:29.987 回答
0

从我的角度来看,唯一正确的答案在这里被否决(尝试 TaskScheduler 的 UnobservedTaskException 事件)

问题是您正在使用无法处理异常的“async void”。这不是 WinRT 的限制,而是异步的设计行为。您需要深入了解 async 才能在此处正确实现异常处理。请参阅这篇文章:http: //msdn.microsoft.com/en-us/magazine/jj991977.aspx

当 GC 收集未观察到的任务时,将重新引发异常。您可以通过注册 TaskScheduler UnobservedTaskException 事件来获取它们。注意 - 异常到达那里需要一些时间,因为垃圾收集器会记录它。

通常不使用'async void',但在 UI 事件处理程序中你必须使用,所以这是你唯一的方法..

于 2013-08-07T14:15:37.750 回答
0

这个问题很老了;尽管如此,一个类似的问题现在困扰着我的 UWP 应用程序。有时我必须通过允许异常向上传播来展开调用堆栈。我需要异常才能到达应用程序的UnhandledException处理程序,但这并不总是发生。此问题包括在非 UI 线程上抛出的明显情况。它还包括一个不明显的案例,我尚未确定其因果关系,即抛出 UI 线程。

我想出了以下解决方案。我捕获异常,然后将其显式发布到同步上下文。由于前面提到的奇怪问题,即使已经在当前同步上下文上运行,我也必须这样做。否则,异常有时不会到达UnhandledException处理程序。

private void Throw( Exception exception )
{
    uiContext.Post( arg => throw exception, null );
    throw new OperationCanceledException();
}
于 2020-02-25T11:24:49.897 回答
-1

尝试 TaskScheduler 的 UnobservedTaskException 事件

于 2012-10-20T20:21:26.513 回答