title: How to avoid cascading error messages
body: <p>I'm trying to design an application error handler that resolves any unhandled exceptions, but there are some cases of undesired behaviour that I can't seem to get around.</p>
  
  <p><code>Application_DispatcherUnhandledException</code> will get called whenever a thread outside of the UI runs into trouble. This will in turn call <code>App.HandleError</code> - a static method which will log the issue, display a message to the user, and if something critical is wrong, initiate a shut down of the application.</p>
  
  <p>My main issue seems to be when something in the xaml starts generating Exceptions (such as an exception in a DataTemplate or Routed Event). In most cases, WPF will just keep trying to generate the control that's throwing the exception over and over, resulting in cascading error messages and the App consuming all processor power until it crashes unceremoniously.</p>
  
  <p><img src="https://i.stack.imgur.com/HfZbV.png" alt="Cascading Error Messages"></p>
  
  <p>I thought I had resolved this in the error handler by locking the method, or by returning right away if the method is already in the middle of executing, but this has two problems - the first is that if the same exception keeps occuring, as soon as the user hits "OK" and execution of the ErrorHandler unlocks, it will just pop up again. I need some way of determining if we're in a cascading error state so I can just initiate a shut down of the application.</p>
  
  <p>The other problem is that in the event two or more separate threads produce different errors simultaneously, I certainly don't want any solution that will mistake this for a cascading / unrecoverable error, and I don't want one of the errors to simply get ignored because the other one got there first.</p>
  
  <p>Any ideas? I've considered things like using Interlocked.Increment on an error count, using the lock() statement, and caching the last few errors with time-stamps, but they all seem to have shortcomings.</p>
  
  <p>Here's my latest attempt. I apologize for how thick it is, but I try to handle quite a few unique problems at once.</p>
  
  <pre><code>private bool DispatchedErrorsLock = false;
  private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
  {
      //Prevent Recursion
      e.Handled = true;
      if( DispatchedErrorsLock || ExceptionHandlingTerminated ) return;
      DispatchedErrorsLock = true;
  
      bool handleSilently = false;
      //Ensures that minor xaml errors don't reset the application
      if( "PresentationFramework,PresentationCore,Xceed.Wpf.DataGrid.v4.3".Split(',').Any(s =&gt; e.Exception.Source.Contains(s)) )
      {
          handleSilently = true;
      }
  
      HandleError(e.Exception, "Exception from external thread.", !handleSilently, !handleSilently);
      DispatchedErrorsLock = false;
  }
  
  private static int SimultaneousErrors = 0;
  private static bool ExceptionHandlingTerminated = false;
  public static void HandleError(Exception ex, bool showMsgBox) { HandleError(ex, "", showMsgBox, true); }
  public static void HandleError(Exception ex, string extraInfo, bool showMsgBox) { HandleError(ex, extraInfo, showMsgBox, true); }
  public static void HandleError(Exception ex, string extraInfo = "", bool showMsgBox = true, bool resetApplication = true)
  {
      if( ExceptionHandlingTerminated || App.Current == null ) return;
      Interlocked.Increment(ref SimultaneousErrors); //Thread safe tracking of how many errors are being thrown
      if( SimultaneousErrors &gt; 3 )
      {
          throw new Exception("Too many simultaneous errors have been thrown.");
      }
  
      try
      {
          if( Thread.CurrentThread != Dispatcher.CurrentDispatcher.Thread )
          {
              //We're not on the UI thread, we must dispatch this call.
              ((App)App.Current).Dispatcher.BeginInvoke((Action&lt;Exception, string, bool, bool&gt;)
                  delegate(Exception _ex, string _extraInfo, bool _showMsgBox, bool _resetApplication)
                  {
                      Interlocked.Decrement(ref SimultaneousErrors);
                      HandleError(_ex, _extraInfo, _showMsgBox, _resetApplication);
                  }, DispatcherPriority.Background, new object[] { ex, extraInfo, showMsgBox, resetApplication });
              return;
          }
  
          if( !((App)App.Current).AppStartupComplete )
          {   //We can't handle errors the normal way if the app hasn't started yet.
              extraInfo = "An error occurred before the application could start." + extraInfo;
              throw ex; //Hack: Using throw as a goto statement.
          }
  
          String ErrMessage = string.Empty;
          if( string.IsNullOrEmpty(extraInfo) &amp;&amp; showMsgBox )
              ErrMessage += "An error occurred while processing your request. ";
          else
              ErrMessage += extraInfo;
  
          if( !showMsgBox &amp;&amp; !resetApplication )
              ErrMessage += " This error was handled silently by the application.";
  
          //Logs an error somewhere.
          ErrorLog.CreateErrorLog(ex, ErrMessage);
  
          if( showMsgBox )
          {
              ErrMessage += "\nTechnical Details: " + ex.Message;
              Exception innerException = ex.InnerException;
              while( innerException != null )
              {   //Add what is likely the more informative information in the inner exception(s)
                  ErrMessage += " | " + ex.InnerException.Message;
                  innerException = innerException.InnerException;
              }
          }
  
          if( resetApplication )
          {
              //Resets all object models to initial state (doesn't seem to help if the UI gets corrupted though)
              ((MUS.App)App.Current).ResetApplication();
          }
          if( showMsgBox )
          {
              //IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442
              //The solution is to dispatch and queue the MessageBox. We must use BeginInvoke() because dispatcher processing is suspended in such cases, so Invoke() would fail..
              Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
              {
                  MessageBox.Show(ErrMessage, "MUS Application Error", MessageBoxButton.OK, MessageBoxImage.Error);
                  Interlocked.Decrement(ref SimultaneousErrors);
              }, DispatcherPriority.Background);
          }
          else
          {
              Interlocked.Decrement(ref SimultaneousErrors);
          }
      }
      catch( Exception e )
      {
          Interlocked.Decrement(ref SimultaneousErrors);
          ExceptionHandlingTerminated = true;
          //A very serious error has occurred, such as the application not loading or a cascading error message, and we must shut down.
          String fatalMessage = String.Concat("An error occurred that the application cannot recover from. The application will have to shut down now.\n\nTechnical Details: ", extraInfo, "\n", e.Message);
          //Try to log the error, but in extreme cases, there's no guarantee logging will work.
          try { ErrorLog.CreateErrorLog(ex, fatalMessage); }
          catch( Exception ) { }
          Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
          {
              MessageBox.Show(fatalMessage, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Stop);
              if( App.Current != null ) App.Current.Shutdown(1);
          }, DispatcherPriority.Background);
      }
  }
  </code></pre>