处理UWP应用程序中的Dispatcher异常

时间:2018-02-23 13:19:32

标签: c# exception-handling uwp

在我的UWP应用程序中,我需要从另一个线程更新UI,我正在使用CoreDispatcher来执行此操作。我希望任何未处理的异常终止应用程序,就像UI线程中的异常一样,但似乎并非如此。下面的RunAsync lambda中抛出的异常将被UWP静默忽略。此外,这些异常永远不会发送到任何应用程序UnhandledException处理程序(以下示例中的第二个例外)。

发生了什么事? Dispatcher是否只捕获所有异常并默默忽略它们?

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;

        Task.Run( async () =>
        {
            await Task.Delay(2000);

            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                throw new Exception("This exception will not terminate the app.");
            });
        });
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        throw new NotImplementedException("This will terminate the app");
    }
}

3 个答案:

答案 0 :(得分:3)

它们在技术上不是无法处理的,因为任务中的异常被“捕获”并包装在Task对象中。如果没有以某种方式从该任务中消耗该异常(通过等待任务或使用.ContinueWith),当任务被垃圾收集时,您可以监听TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;以获取该异常 - 您可以手动终止该应用程序如果你愿意或重新抛出,那就从那里开始。

当然要记住,根据你编写代码的方式,不能保证任务会被任何急速垃圾收集,或者根本不可能 - 所以如果你在任何地方存储对任务的引用,那么很可能如果你不小心的话,默默地吞下这些例外。

答案 1 :(得分:1)

  

发生了什么事? Dispatcher是否只捕获所有异常并默默忽略它们?

不是Dispatcher问题。问题是当运行Task时,它抛出的任何异常都会在任务完成时保留并重新抛出。因此,如果您想要终止应用程序的异常,则需要等待Task完成。

目前,您可以使用await关键字等待任务变为现在。

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    var dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;

    await Task.Run(async () =>
      {
          await Task.Delay(2000);

          await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
          {
              throw new NotImplementedException("This exception also terminate the app.");
          });
      });

}

答案 2 :(得分:0)

要处理来自其他线程的错误,只需使用ContinueWith函数即可。您的代码可以是:

   Task.Run(async () =>
    {
        await Task.Delay(2000);
        throw new Exception("This exception will terminate the app.");
    })
    .ContinueWith(async (res) =>
    {
        if (res.IsFaulted)
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                () => throw res.Exception.InnerException);
    });