tags:

views:

280

answers:

5

Note, I realize that this has been addressed here. That post discusses exception handling in .NET 1.1 while implying that there is a better solution for >.NET 2.0 so this question is specifically about the more recent .NET versions.

I have a windows forms application which is expected to frequently and unexpectedly lose connectivity to the database, in which case it is to reset itself to its initial state.

I am already doing error logging, retry connection, etc. through a set of decorators on my custom DBWrapper object. After that is taken care of however, I would like to let the error fall through the stack. Once it reaches the top and is unhandled I would like it to be swallowed and my ApplicationResetter.Reset() method to be executed.

Can anyone tell me how to do this?

If this is impossible, then is there at least a way to handle this without introducing a dependency on ApplicationResetter to every class which might receive such an error and without actually shutting down and restarting my application (which would just look ugly)?

+2  A: 

caveat: not familiar with 3.5 yet, there may be a better answer ...

...but my understanding is that by the time the event gets to the unhandled exception handler, the app is probably going to die - and if it doesn't die, it may be so corrupted that it should die anyway

if you are already handling a db-not-there case and are letting other exceptions pass through, then the app should die as it may be unstable

Steven A. Lowe
I am handling the case by retrying and logging it, however after that is done my DBWrapper.GetDataSet() still has to either return something to the client or throw an error. If I throw then the error will bubble up unless I catch which needs a resetter dependency. I want that as high as possible.
George Mauer
if you fail to find the database after, say, three wait-and-retry cycles, then return null and let the caller check for that. Otherwise, throw a custom exception and let the app die - no application can compensate for an unstable/unreachable database.
Steven A. Lowe
but thats exactly the point, I can return null or I can throw an error, either way the calling class has to take an ApplicationResetter dependency
George Mauer
i think i'm confused as to your purpose. If the database is gone and you want ApplicationResetter to kick in and restart the app (or something like that) why not just let DBWrapper.GetDataSet do it directly?
Steven A. Lowe
+1  A: 

Perhaps the Application.ThreadException event will suit your needs:

static void Main()
{
    Application.ThreadException += Application_ThreadException;
    //...
 }

 static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
 {
     // call ApplicationResetter.Reset() here
 }
Duncan Smart
I'm not sure if this will work, won't my application terminate anyways?
George Mauer
er, no it won't!
Duncan Smart
my mistake, i was thinking of AppDomain.CurrentDomain.UnhandledException
Steven A. Lowe
+1  A: 

There are the System.Windows.Forms.Application.ThreadException event and the System.AppDomain.CurrentDomain.UnhandledException events.

As mentioned by Steven, these will leave your application in an unknown state. There's really no other way to do this except putting the call that could throw the exception in a try/catch block.

Max Schmeling
A: 

A pretty in-depth explanation of unhandled exceptions in the latest MSDN issue: September 2008

A: 

For Windows Forms threads (which call Application.Run()), assign a ThreadException handler at the beginning of Main(). Also, I found it was necessary to call SetUnhandledExceptionMode:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.Automatic);
Application.ThreadException += ShowUnhandledException;
Application.Run(...);

Here is an example handler. I know it's not what you're looking for, but it shows the format of the handler. Notice that if you want the exception to be fatal, you have to explicitly call Application.Exit().

static void ShowUnhandledException(object sender, ThreadExceptionEventArgs t)
{
 Exception ex = t.Exception;
 try {
  // Build a message to show to the user
  bool first = true;
  string msg = string.Empty;
  for (int i = 0; i < 3 && ex != null; i++) {
   msg += string.Format("{0} {1}:\n\n{2}\n\n{3}", 
    first ? "Unhandled " : "Inner exception ",
    ex.GetType().Name,
    ex.Message, 
    i < 2 ? ex.StackTrace : "");
   ex = ex.InnerException;
   first = false;
  }
  msg += "\n\nAttempt to continue? (click No to exit now)";

  // Show the message
  if (MessageBox.Show(msg, "Unhandled exception", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.No)
   Application.Exit();
 } catch (Exception e2) {
  try {
   MessageBox.Show(e2.Message, "Fatal error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
  } finally {
   Application.Exit();
  }
 }
}
Qwertie