请考虑以下内容从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();
}
}
因此,给定具有两个按钮及其单击事件处理程序的Metro UI,唯一的区别是第二个事件处理程序被标记为async
。
然后单击每个按钮,我希望在两个情况下调用UnhandledException处理程序,因为它们(应该)都是通过UI线程和相关的同步上下文输入的。
我的理解是,对于async void
方法,应该通过初始同步上下文捕获和“重新抛出”任何异常(保留原始堆栈跟踪),这也在Async / Await FAQ中明确说明。
但是在async
的情况下,UnhandledException处理程序被而不是调用,因此应用程序崩溃了!由于这会挑战我认为非常直观的模型,我需要知道原因!是的,我知道我可以将处理程序的主体包装在try { } catch { }
中,但我的问题是为什么不调用逆止器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中将该异常标记为'processed',它与Metro / WinRT应用程序对齐上面的UnhandledException事件处理程序。]
然后单击每个按钮,DispatcherUnhandledException处理程序确实在两个情况下被调用,正如预期的那样,并且应用程序不崩溃。
答案 0 :(得分:5)
在这里回答:No UnhandledException fired from async event callback
这是WinRT的已知限制。希望它在下次更新时得到修复。
答案 1 :(得分:5)
以下帖子中的解决方案为我工作,只做了一个小改动:我不得不移动AsyncSynchronizationContext.Register();到App.OnLaunched活动
答案 2 :(得分:3)
如文档中所述(来源:http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.application.unhandledexception.aspx):
重要的是要意识到这个问题的一些局限性 Application.UnhandledException事件。此事件仅用于 XAML框架遇到的异常。遇到例外情况 由其他Windows运行时组件或应用程序的一部分 未连接到XAML框架不会导致此事件 被抚养长大。
例如,如果其他Windows组件调用 进入应用程序代码并抛出异常并且没有被捕获 不会引发UnhandledException事件。如果应用程序创建 一个工作线程,然后在工作线程上引发一个异常,
。不会引发UnhandledException事件。
正如在this conversation中所指出的,检索工作线程中发生的异常的唯一方法是将它们包装在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();
}
底线是记录应用中的所有错误,您应该:
答案 3 :(得分:0)
从我的观点来看,唯一正确的回答是在这里被downvoted(尝试TaskScheduler的UnobservedTaskException事件)
问题是您使用的是'async void',无法处理异常。这不是WinRT限制,而是async的设计行为。您需要深入了解异步以正确实现异常处理。请参阅此文章:http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
如果GC收集任务未被观察到,则会重新发生异常。您可以通过注册TaskScheduler UnobservedTaskException事件来获取它们。注意 - 在异常到达之前需要一些时间,因为它用垃圾收集器记录。
一般不要使用'async void',但是在UI事件处理程序中你必须这样做,所以这是你唯一的方法..
答案 4 :(得分:0)
这个问题很旧;尽管如此,现在类似的问题困扰着我的UWP应用。有时,我必须通过允许异常向上传播来释放调用堆栈。我需要异常才能到达应用程序的UnhandledException
处理程序,但这并不总是发生。此问题包括抛出非UI线程的明显情况。它也包括一个不明显的情况,引发UI线程。我尚未确定原因。
我想到了以下解决方案。我捕获了异常,然后将其显式发布到同步上下文。由于上述奇怪的问题,即使已经在当前同步上下文中运行,我也必须这样做。否则,异常有时不会到达UnhandledException
处理程序。
private void Throw( Exception exception )
{
uiContext.Post( arg => throw exception, null );
throw new OperationCanceledException();
}
答案 5 :(得分:-2)
尝试使用TaskScheduler
的UnobservedTaskException事件