Search code examples
.netwpfexceptionsystem.reactive

The same exception thrown twice crashes WPF


We seem to be having some "interesting" behavior with unhandled exceptions in WPF.

In a nutshell, the same exception thrown twice on the dispatcher thread in a short period of time will bypass the dispatcher unhandled exception handler and bring down the application.

Reproduce Steps

  • 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);
    

You will notice that the DispatcherUnhandledException handler is raised twice, both exceptions handled, and all is well.

However, change the code above to the following:

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

And you will find that AppDomain.CurrentDomain.UnhandledException handler is raised and the application will crash with the typical windows "would you like to debug" dialog.

Extra Info

It may seem that this example is contrived, which it is just to simplify the question. This scenario can happen however if say you have two RX subscribers to a stream which errors. In this case both subscribers raise the same exception causing the same behavior as above. For instance, the following RX code in the button click handler will also reproduce the issue (also contrived but you can get into equivalent situations):

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

Solution

  • It seems that this is Dispatcher behaviour, it will check to see if it has "seen" the exception before (by adding a tag into the exception's data) and only handle it if it hasn't:

     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

    What this means is we are likely going to have to catch and re-wrap exceptions (i.e creating new exception objects so the dispatcher doesn't see them as the same) so that they do not bring the application down.