简体   繁体   中英

WinForms Global Exception Handling?

I have implemented a software which have a DLL library which contains a huge set of classes which includes all the methods for my software.

Now i want to be able to handle some global errors like error #26 which is a no Network Related Error on all these classes instead of going to each class and add it. How to do that ?

If #26 is an exception then you can use AppDomain.CurrentDomain.UnhandledException event. If it's just a return value, then I don't see any chance to handle that globally.

public static void Main()
{
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

    // start main thread here
}

static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
{
    Exception e = (Exception) args.ExceptionObject;
    Console.WriteLine("MyHandler caught : " + e.Message);
}

Since its a winforms app you could just enclose Application.Run(new MainForm()); in a try catch block.

static void Main()
{
try {
 Application.Run(new MainForm());
} catch(SystemException)//just as an example 
{
 //log or handle the error here. 
}
}

I don't know any implications this kind of solution would cause, but I just told you what you needed.

Other options are subscribing to Application.ThreadException event.

Read more here: unhandledexceptions

There is also AppDomain.UnhandledException and you should read the difference between them here on MSDN .

From MSDN :

For certain application models, the UnhandledException event can be preempted by other events if the unhandled exception occurs in the main application thread.

In applications that use Windows Forms, unhandled exceptions in the main application thread cause the Application.ThreadException event to be raised. If this event is handled, the default behavior is that the unhandled exception does not terminate the application, although the application is left in an unknown state. In that case, the UnhandledException event is not raised. This behavior can be changed by using the application configuration file, or by using the Application.SetUnhandledExceptionMode method to change the mode to UnhandledExceptionMode.ThrowException before the ThreadException event handler is hooked up. This applies only to the main application thread. The UnhandledException event is raised for unhandled exceptions thrown in other threads.

With the reference of Centralized Exception Handling in C# Windows Application, I found one of good solution :

static class Program
{
    [STAThread]
    static void Main()
    {
        // Add handler to handle the exception raised by main threads
        Application.ThreadException += 
        new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

        // Add handler to handle the exception raised by additional threads
        AppDomain.CurrentDomain.UnhandledException += 
        new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());

        // Stop the application and all the threads in suspended state.
        Environment.Exit(-1);            
    }

    static void Application_ThreadException
        (object sender, System.Threading.ThreadExceptionEventArgs e)
    {// All exceptions thrown by the main thread are handled over this method

        ShowExceptionDetails(e.Exception);
    }

    static void CurrentDomain_UnhandledException
        (object sender, UnhandledExceptionEventArgs e)
    {// All exceptions thrown by additional threads are handled in this method

        ShowExceptionDetails(e.ExceptionObject as Exception);

        // Suspend the current thread for now to stop the exception from throwing.
        Thread.CurrentThread.Suspend();
    }

    static void ShowExceptionDetails(Exception Ex)
    {
        // Do logging of exception details
        MessageBox.Show(Ex.Message, Ex.TargetSite.ToString(), 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}  

In the above class, we shall attach an event handler to two events. It is better to attach these events as soon as the main method starts.

Application.ThreadException - This event will be raised when an exception is thrown in the main thread. If we add an event handler, then the exception is handled over the method.

AppDomain.CurrentDomain.UnhandledException - This event will be raised when an exception is thrown in the additional threads used in the application. The worse scenario here is as soon as the handlers' execution gets over, the exception is again thrown whereas the application ends. This need to be handled. Here I have used a bit of code to handle this situation and continue the execution of the application without interruption.

The logic I have used to overcome this situation is just suspending the thread in the event handler, so that the application continues to work fine. Again a problem arises in suspending this thread. When the main form is closed, the application normally needs to exit, but as the thread is in suspended state, the application will still remain running. So to exit the application completely and stop the process, Environment.Exit(-1) must be called before the ending of the main method.

First, You should add:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

After that You can catch exceptions, for example:

[STAThread]
    static void Main()
    {

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        Application.ThreadException += ApplicationThreadException;
        AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

        Application.Run(new MainForm());
    }

    /// <summary>
    /// Global exceptions in Non User Interface (other thread) handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        var message =
            String.Format(
                "Sorry, something went wrong.\r\n" + "{0}\r\n" + "{1}\r\n" + "Please contact support.",
                ((Exception)e.ExceptionObject).Message, ((Exception)e.ExceptionObject).StackTrace);
        MessageBox.Show(message, @"Unexpected error");
    }

    /// <summary>
    /// Global exceptions in User Interface handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
    {
        var message =
            String.Format(
                "Sorry, something went wrong.\r\n" + "{0}\r\n" + "{1}\r\n" + "Please contact support.",
                e.Exception.Message, e.Exception.StackTrace);
        MessageBox.Show(message, @"Unexpected error");
    }

Global error interception in winforms

    static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        try
        {
            Application.Run(new myForm());
        }
        catch (Exception ex)
        {
            HandleException(ex);
        }
    }

    internal static void HandleException(Exception ex)
    {
        string LF = Environment.NewLine + Environment.NewLine;
        string title = $"Oups... I got a crash at {DateTime.Now}";            
        string infos = $"Please take a screenshot of this message\n\r\n\r" +
                       $"Message : {LF}{ex.Message}{LF}" +
                       $"Source : {LF}{ex.Source}{LF}" +
                       $"Stack : {LF}{ex.StackTrace}{LF}" +
                       $"InnerException : {ex.InnerException}";

        MessageBox.Show(infos, title, MessageBoxButtons.OK, MessageBoxIcon.Error); // Do logging of exception details
    }
}

As an extension of what is shown above, I use the following:

try
{
    Application.Run(new FormMain());
}
catch (Exception ex)
{
    RoboReporterConstsAndUtils.HandleException(ex);
}

...where the HandleException() method can be something like:

internal static void HandleException(Exception ex)
{
    var exDetail = String.Format(ExceptionFormatString,
        ex.Message,
        Environment.NewLine,
        ex.Source,
        ex.StackTrace,
        ex.InnerException);

    ExceptionLoggingService.Instance.LogAndEmailExceptionData(string.Format("{0}: {1}: {2}",
        DateTime.Now.ToLongDateString(), GetVersionInfo(), exDetail));
}

Another way to skin this cat is:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    AppDomain.CurrentDomain.UnhandledException += Unhandled;
    Application.Run(new FormMain());
}

static void Unhandled(object sender, UnhandledExceptionEventArgs exArgs)
{
    ExceptionLoggingService.Instance.LogAndEmailMessage(String.Format
        ("From application-wide exception handler: {0}", exArgs.ExceptionObject));
}

Of course, you can do whatever you want within the Unhandled() method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM