3

我们似乎在 WPF 中有一些“有趣”的行为,其中包含未处理的异常。

简而言之,在短时间内在调度程序线程上抛出两次相同的异常将绕过调度程序未处理的异常处理程序并关闭应用程序。

重现步骤

  • 创建一个新的 WPF 应用程序
  • 创建一个 DispatcherUnhandledException 处理程序,将 e.Handled 设置为 true 并打开一个显示异常的消息框。
  • 创建一个 AppDomain.CurrentDomain.UnhandledException 处理程序,也从此处显示一个消息框(请注意,您无法在此处处理异常,因此此处出现的任何异常都意味着应用程序即将关闭)。
  • 现在添加一个按钮,并在点击处理程序中添加以下内容:

    SynchronizationContext.Current.Post(s => { throw new Exception(); }, null);
    SynchronizationContext.Current.Post(s => { throw new Exception(); }, null);
    

您会注意到 DispatcherUnhandledException 处理程序被引发了两次,处理了两个异常,一切都很好。

但是,将上面的代码更改为以下内容:

    var ex = new Exception();
    SynchronizationContext.Current.Post(s => { throw ex; }, null);
    SynchronizationContext.Current.Post(s => { throw ex; }, null);

您会发现 AppDomain.CurrentDomain.UnhandledException 处理程序被引发,应用程序将在典型的窗口“您要调试”对话框中崩溃。

额外信息

这个例子似乎是人为的,只是为了简化问题。但是,如果假设您有两个 RX 订阅者到一个错误的流,则可能会发生这种情况。在这种情况下,两个订阅者都会引发相同的异常,从而导致与上述相同的行为。例如,按钮单击处理程序中的以下 RX 代码也将重现该问题(也是人为的,但您可以进入等效情况):

        var o = Observable.Start(() => { throw new Exception(); }).Publish();
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.Connect();
4

1 回答 1

3

这似乎是 Dispatcher 的行为,它会检查它之前是否“看到”异常(通过在异常的数据中添加标签)并且只有在没有的情况下才处理它:

 private bool ExceptionFilter(Exception e)
    {
        // see whether this dispatcher has already seen the exception.
        // This can happen when the dispatcher is re-entered via
        // PushFrame (or similar).
        if (!e.Data.Contains(ExceptionDataKey))
        {
            // first time we've seen this exception - add data to the exception
            e.Data.Add(ExceptionDataKey, null);
        }
        else
        {
            // we've seen this exception before - don't catch it
            return false;
        }
        ....

http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/wpf/src/Base/System/Windows/Threading/Dispatcher@cs/1407647/调度员@cs

这意味着我们很可能必须捕获并重新包装异常(即创建新的异常对象,这样调度程序就不会认为它们是相同的),这样它们才不会导致应用程序崩溃。

于 2013-01-21T10:02:09.200 回答