两次抛出的相同异常使 WPF 崩溃

Posted

技术标签:

【中文标题】两次抛出的相同异常使 WPF 崩溃【英文标题】:The same exception thrown twice crashes WPF 【发布时间】:2013-01-02 02:08:09 【问题描述】:

我们似乎在 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();

【问题讨论】:

【参考方案1】:

这似乎是 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/Dispatcher@cs

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

【讨论】:

以上是关于两次抛出的相同异常使 WPF 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

对话框片段已添加异常未抛出

java中如何一次抛出多个异常

如何防止后台线程抛出的异常让程序崩溃退出

重载和重写

如何调试 WPF InitializeComponent() 中抛出的 EEFileLoadException?

消息驱动 Bean 两次读取相同的消息