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)?
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();
}
}
}
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
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
}
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.
A pretty in-depth explanation of unhandled exceptions in the latest MSDN issue:
September 2008