Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement one "catch'em all" exception handler with resume?

Tags:

c#

exception

If you are running a Windows Forms application: add a handler to the Application.ThreadException event.


I assume you are writing a Windows application in which case, yes, you can do this. I will leave the rights and wrongs of whether or not you should to others. There are already enough answers which look at this and I suggest you consider them carefully before you actually do this.

Note, that this code will behave differently in the debugger than it does if you run the application directly (another reason not to do it perhaps). To get the application to show the messagebox and to continue on thereafter you will need to run the application from explorer, not from visual studio.

Create a new Windows forms application. The code in Program.cs looks something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication2 {
    static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Form1 form1 = new Form1();
            Application.ThreadException += new ThreadExceptionEventHandler(form1.UnhandledThreadExceptionHandler);
            Application.Run(form1);
        }
    }
}

Then make the code in Form1 look something like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication2 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        public void UnhandledThreadExceptionHandler(object sender, ThreadExceptionEventArgs e) {
            this.HandleUnhandledException(e.Exception);
        }

        public void HandleUnhandledException(Exception e) {
            // do what you want here.
            if (MessageBox.Show("An unexpected error has occurred. Continue?",
                "My application", MessageBoxButtons.YesNo, MessageBoxIcon.Stop,
                MessageBoxDefaultButton.Button2) == DialogResult.No) {
                Application.Exit();
            }
        }

        private void button1_Click(object sender, EventArgs e) {
            throw new ApplicationException("Exception");
        }

    }
}

(Add button1 to the form and attach it button1_Click.)


It depends on what you mean by "resume". The trouble with exceptions is that unless you're very careful, by the time an exception happens your application state is quite possibly corrupt - you might have completed half an operation.

If you can isolate your operations - much like a database isolates transactions - then you can effectively let your user resume from the "last commit point". That will very much depend on the type of your application though. Could you give us more details about the kind of application you're building?


Use below code in your program.cs class. It will automatically Send mail when exception occurs.

using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Mail;
using System.Threading; 

namespace ExceptionHandlerTest
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.ThreadException +=
                new ThreadExceptionEventHandler(Application_ThreadException);

            // Your designer generated commands.
        }

        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) 
        {

            var fromAddress = new MailAddress("your Gmail address", "Your name");
            var toAddress = new MailAddress("email address where you want to receive reports", "Your name");
            const string fromPassword = "your password";
            const string subject = "exception report";
            Exception exception = e.Exception;
            string body = exception.Message + "\n" + exception.Data + "\n" + exception.StackTrace + "\n" + exception.Source;

            var smtp = new SmtpClient
            {
                Host = "smtp.gmail.com",
                Port = 587,
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
            };
            using (var message = new MailMessage(fromAddress, toAddress)
            {
                Subject = subject,
                Body = body
            })
            {
                //You can also use SendAsync method instead of Send so your application begin invoking instead of waiting for send mail to complete. SendAsync(MailMessage, Object) :- Sends the specified e-mail message to an SMTP server for delivery. This method does not block the calling thread and allows the caller to pass an object to the method that is invoked when the operation completes. 
                smtp.Send(message);
            }
        }
    }
}

I don't think this is really feasible using a global error handler. You need to figure out what kind of errors are recoverable at different points in your application and write specific error handlers to address the errors as they occur -- unless you want to resort to application restart, which may or may not work depending on what the actual error is. In order to do any kind of resume, you'll need to save enough state to restart from a known good state.