【问题标题】:Catch-all Exception Handler for non-UI Threads in WPFWPF 中非 UI 线程的全能异常处理程序
【发布时间】:2016-01-10 09:49:44
【问题描述】:

具体来说,我将 WPF 与 MVVM 一起使用。我有一个 MainWindow,它是一个 WPF 窗口,所有操作都在其中发生。它为其属性、命令等使用相应的 View Model 类。

我在 Application.xaml.vb StartUp 中设置了主 UI 线程和非 UI 线程异常处理程序,如下所示:

Private Sub Application_DispatcherUnhandledException(sender As Object, e As Windows.Threading.DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
    ' catches main UI thread exceptions only
    ShowDebugOutput(e.Exception)
    e.Handled = True
End Sub

Private Sub Application_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup
    ' catches background exceptions
    Dim currentDomain As AppDomain = AppDomain.CurrentDomain
    AddHandler currentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
    AddHandler System.Threading.Tasks.TaskScheduler.UnobservedTaskException, AddressOf BackgroundTaskExceptionHandler
End Sub

Sub UnhandledExceptionHandler(sender As Object, args As UnhandledExceptionEventArgs)
    Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
    ShowDebugOutput(ex)
End Sub

Sub BackgroundTaskExceptionHandler(sender As Object, args As System.Threading.Tasks.UnobservedTaskExceptionEventArgs)
    Dim ex As Exception = DirectCast(args.Exception, Exception)
    ShowDebugOutput(ex)
End Sub

这部分有效

当我尝试通过故意抛出异常来测试它时,它可以工作。它实际上是在处理 Select All 按钮单击的 Sub 中的 View Model 中。

按钮:

<Button Content="Select All" Height="23" Width="110" Command="{Binding SelectAllCommand}" />

我抛出异常的命令被成功捕获:

Private Sub SelectAll()
    Throw (New Exception("UI Thread exception"))
    SetAllApplyFlags(True)
End Sub

这部分不起作用

在同一个 MainWindow 中还有另一个按钮类似地绑定到一个命令。但是,它使用一个任务在后台执行其工作,并且在那里抛出的异常不会被我的 catch-all 处理程序捕获。

Private Sub GeneratePreview()
    ' run in background
    System.Threading.Tasks.Task.Factory.StartNew(
        Sub()
            ' ... stuff snipped out, issue is the same with or without the rest of the code here ...
            Throw (New Exception("Throwing a background thread exception"))
        End Sub)
End Sub

有几个类似的问题,但我无法从它们中真正找出答案。在大多数情况下,AppDomain UnhandledException 似乎是答案,但它不适合我。我究竟需要添加什么才能以这种方式捕获可能在非 UI 线程中抛出的异常?

我最终做了什么

当我在 Application.xaml.vb 中处理 TaskScheduler.UnobservedTaskException 事件时,我无法调用我的事件处理程序。但我从另一个答案中得到了提示,我将其标记为答案,因为它最终有所帮助。

但是,它不在应用程序级别,所以如果这是一个更大的应用程序,我必须在我使用任务的每个实例中复制它。这不是我真正想要的,但现在不愿意花更多时间。

我最终在任务中放置了一个try-catch 。在捕获中,我能够使用 Dispatcher.Invoke 仍然显示带有异常信息的用户友好对话框。

Private Sub GeneratePreview()
    ' run in background
    System.Threading.Tasks.Task.Factory.StartNew(
        Sub()
            Try
                ' ... stuff snipped out, issue is the same with or without the rest of the code here ...
                Throw (New Exception("Throwing a background thread exception"))
            Catch ex As Exception
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, DirectCast(
                    Sub()
                        HRNetToADImport.Application.ShowDebugOutput(ex)
                    End Sub, Action))
            End Try
        End Sub)
End Sub

【问题讨论】:

  • 可能重复。 stackoverflow.com/questions/1472498/… ... 我认为@Drew Noakes 的答案是最好的答案。
  • 啊哈。我看到了这个问题,但没有在 TaskScheduler.UnobservedTaskException 部分触发。那么我可以在 Application.xaml.vb 中添加这个处理程序 - 整个应用程序的一个处理程序吗?
  • @cscmh99 你能详细说明我如何使用它吗?我尝试在 StartUp 的 Application.xaml.vb 中添加一个处理程序:AddHandler System.Threading.Tasks.TaskScheduler.UnobservedTaskException,AddressOf BackgroundTaskExceptionHandler,但它仍然没有被调用。当我抛出那个后台异常时。
  • 你能再等一会儿看看事件是否触发吗?该事件可能不会立即触发(可能会延迟几秒钟)。对不起,我不是一个真正的VB人。但我在 C# 中测试它就像你所做的一样,它确实有效。还要在 App 构造函数中注册事件。

标签: wpf vb.net mvvm exception-handling unobserved-exception


【解决方案1】:

桑德拉,

有一种方法可以从后台任务中捕获异常。
我承认我的解决方案不是全球性的,但至少它可以捕获并防止崩溃!

Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
    Dim t As Task = Task.Factory.StartNew(
       Sub()
           ' ... stuff snipped out, issue is the same with or without the rest of the code here ...
           Throw (New Exception("Throwing a background thread exception"))
       End Sub)
    Try
        Await t
    Catch ex1 As Exception
        Debug.WriteLine("Exception from inside task " & ex1.Message)
    End Try
End Sub

认为它可能会有所帮助,可能是你,也可能是其他人。

【讨论】:

  • 谢谢,我相信它会帮助别人。就我而言,我真的很想在全球范围内这样做。我已经为所有我认为可能得到的地方捕获了异常。这是针对我可能错过的任何意外情况。
【解决方案2】:

TaskScheduler.UnobservedTaskException 事件是你想从 App start 订阅的。

https://msdn.microsoft.com/en-us/library/vstudio/system.threading.tasks.taskscheduler.unobservedtaskexception(v=vs.100).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

当故障任务的未观察到的异常即将触发异常升级策略时发生,默认情况下,该策略将终止进程。

此 AppDomain 范围的事件提供了一种机制来防止异常升级策略(默认情况下终止进程)触发。

注意:事件可能不会立即触发(可能会延迟几秒钟)。您可以想象在到达 UnobservedTaskException 事件之前,有一些调用堆栈冒泡和正常异常操作的上下文切换的操作。

我想指出的一点是,必须使用通用异常处理程序包装整个应用程序,以防止应用程序被终止。但是,请记住,对所有可能引发异常的路径实施适当的异常处理也是必须的。

【讨论】:

  • 谢谢。我已经编辑了我的帖子以包含我添加的处理程序。我还运行了编译程序以避免 Visual Studio 在未处理的异常上停止,但没有区别。无论我等待多长时间,处理程序都不会触发。如果你有 C# 代码不用担心,我可以翻译。
【解决方案3】:

桑德拉,

我看了cscmh99命题, 拿了你的代码,然后尝试运行,它可以工作!

我的意思是你可以订阅TaskScheduler.UnobservedTaskException

  • 那你会赶上UnobservedException

  • 但您不会捕获观察到的异常
    观察到的异常是来自使用 .Wait().Result 等待的任务中的异常

Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Here follows an Unobserved Exception

    System.Threading.Tasks.Task.Factory.StartNew(
        Sub()
            Throw (New Exception("Throwing a background thread exception"))
        End Sub)

    ' Here follows an ObservedException
    ' ObservedException because there is call to .Wait() (or .Result)
    ' You can't use UnobservedException for that one

    Dim t As Task = System.Threading.Tasks.Task.Factory.StartNew(
        Sub()
            Throw (New Exception("Throwing a background thread exception"))
        End Sub)
    t.Wait()
End Sub

这是工作解决方案的代码:http://1drv.ms/1XOvTbK

问候

【讨论】:

  • 哈。我的代码对我不起作用,但我看不出我所做的与您在示例中所拥有的之间的区别。我的异常应该是未观察到的,因为我没有在等待它,而且我已经订阅了 UnobservedTaskException。我唯一能想到的是,就我而言,我不是从代码隐藏中抛出,而是从 ViewModel 中抛出。触发时事件的范围可能不够广泛,无法到达 Application.xaml.vb 中的处理程序。或者... WithEvents!既然这是一个事件......可能就是这样。下周试试。谢谢!
  • 我想弄清楚:你在运行另一个 AppDomain 吗?你说你的代码是在 ViewModel 中,好的 - 它是另一个里面的任务(所以它可能是另一个 TaskScheduler)?
猜你喜欢
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 2011-04-07
  • 2010-12-01
  • 1970-01-01
相关资源
最近更新 更多