Windows 8 未为Metro/WinRT UI异步无效事件处理程序调用未处理的异常处理程序

Windows 8 未为Metro/WinRT UI异步无效事件处理程序调用未处理的异常处理程序,windows-8,microsoft-metro,windows-runtime,async-await,c#-5.0,Windows 8,Microsoft Metro,Windows Runtime,Async Await,C# 5.0,将以下内容视为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

将以下内容视为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

然后单击每个按钮,我希望在两种情况下都调用未处理的异常处理程序,因为它们(应该)都是通过UI线程和关联的同步上下文输入的。 我的理解是,对于
async void
方法,任何异常都应该被捕获,并通过初始同步上下文“rethrown”(保留原始stacktrace),这在

但是未处理的异常处理程序在
async
案例中未被调用,因此应用程序崩溃!由于这个挑战,我认为另一个非常直观的模型,我需要知道为什么!是的,我知道我可以用
try{}catch{}
来包装处理程序的主体,但我的问题是为什么不调用backstop UnhandledException处理程序

为了进一步强调为什么这没有意义,考虑下面的WPF应用程序中的几乎相同的摘录,也使用AsiNc/Acess和Abjest.NET框架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既有应用程序DispatcherUnhandledException事件处理程序,也有AppDomain UnhandledException事件处理程序,这有一个细微的区别,但您只能在DispatcherUnhandledException中将异常标记为“已处理”,这与上面的Metro/WinRT应用程序UnhandledException事件处理程序一致。]

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

回答如下:


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

尝试TaskScheduler的UnobservedTaskException事件,如文档中所述(来源:):

重要的是要意识到 Application.UnhandledException事件。此事件仅与一起使用 XAML框架遇到的异常。遇到的异常 由应用程序的其他Windows运行时组件或部分 未连接到XAML框架不会导致此事件 被抚养

例如,如果不同的Windows组件调用 在应用程序代码中引发异常,但未捕获异常时 不会引发未处理的异常事件。如果应用程序创建 工作线程,然后在工作线程上引发异常, 不会引发未处理的异常事件

正如中所指出的,检索工作线程中发生的异常的唯一方法是将它们包装在try/catch块中。因此,以下是我在应用程序中使用的解决方法:我使用以下方法,而不是使用
任务。运行
或等效程序从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();
}
//
///在工作线程中运行代码并检索相关异常(如果有)。
/// 
///目标。
///行动。
公共静态void安全运行(此DependencyObject目标,操作)
{
Task=ThreadPool.RunAsync(o=>
{
尝试
{
动作();
}
捕获(例外情况除外)
{
/*调用与此处未处理的Exception事件处理程序中相同的错误日志记录逻辑*/
}
}).AsTask();
task.Wait();
}
底线是,要记录应用程序中的所有错误,您应该:

  • 订阅未处理的异常事件以获取与XAML堆栈相关的错误
  • 在try/catch块中包装工作线程中发生的操作

  • 在我看来,唯一正确的Answare在这里被否决了(尝试TaskScheduler的UnobservedTaskException事件)

    问题是您使用的是“async void”,其中无法处理异常。这不是WinRT限制,而是异步的设计行为。您需要深入理解异步,才能在这里正确地实现异常处理。见本文:

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


    通常不使用“async void”,但在UI事件处理程序中必须使用,因此这是您唯一的方法。

    以下帖子中的解决方案对我有效,只做了一个小改动:我必须移动AsyncSynchronizationContext.Register();到App.OnLaunched事件


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

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

    private void Throw( Exception exception )
    {
        uiContext.Post( arg => throw exception, null );
        throw new OperationCanceledException();
    }
    

    谢谢你的链接。不幸的是,它没有我所说的答案。我也发现了,所以我不是唯一一个质疑这种行为的人。一个小鸟告诉我,这是一个已知的问题,并没有得到及时修复发布。如果微软内部有人能回答/评论,我将不胜感激