Is there a way to define an action for unhandled e

2020-04-12 16:51发布

问题:

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)?

回答1:

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();
        }
    }
}


回答2:

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



回答3:

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
 }


回答4:

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.



回答5:

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