相同的异常被抛出两次会导致WPF崩溃

3

我们在WPF中遇到了一些有趣的未处理异常行为。

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

复现步骤

  • Create a new WPF Application
  • Create a DispatcherUnhandledException handler, set e.Handled to true and open a message box displaying the exception.
  • Create a AppDomain.CurrentDomain.UnhandledException handler, show a message box from here too (note that you can't handle exceptions here so anything that makes it in here implies the app is about to go down).
  • Now add a button, and in the click handler add the following:

    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处理程序被触发,应用程序将崩溃,并出现典型的Windows“是否要调试”对话框。

额外信息

这个例子似乎是人为制造的,只是为了简化问题。然而,如果你有两个RX订阅者订阅了一个错误流,那么就会发生这种情况。在这种情况下,两个订阅者都会引发相同的异常,导致与上述相同的行为。例如,按钮单击处理程序中的以下RX代码也会重现此问题(同样是人为制造的,但你可能会遇到等效的情况):

        var o = Observable.Start(() => { throw new Exception(); }).Publish();
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.Connect();
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/Dispatcher@cs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接