星期日, 5月 08, 2016

[C#] Global Exception Handler

要在 WinForm 內建立 Global Exception Handler 機制,避免 Exception 沒有處理到,而直接拋給使用者,這篇 MSDN Application.ThreadException 事件 內範例來看就是要處理
  • AppDomain.unhandleException event 針對 non-UI thread exceptions
  • Application.ThreadException event 針對 UI thread exceptions
下面 Code 是從 MSDN 文章內抓出來,稍微整理過,方便閱讀和了解

[C#] Global Exception Handler-0


Programs.cs Code
using System.Diagnostics;
using System.Security.Permissions;
using System.Threading;

namespace GlobalErrorHandle
{
    static class Program
    {

        ///         
        /// 應用程式的主要進入點。
        /// 
        [STAThread]
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
        static void Main()
        {
            // Add the event handler for handling UI thread exceptions to the event.
            Application.ThreadException += Application_ThreadException;
            // Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            // Add the event handler for handling non-UI thread exceptions to the event. 
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ErrorHandlerForm());
        }

        // Handle the UI exceptions by showing a dialog box, and asking the user whether
        // or not they wish to abort execution.
        // NOTE: This exception cannot be kept from terminating the application - it can only 
        // log the event, and inform the user about it. 
        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                Exception ex = (Exception)e.ExceptionObject;
                string errorMsg = "An application error occurred. Please contact the adminstrator with the following information:\n\n";

                // Since we can't prevent the app from terminating, log this to the event log.
                if (!EventLog.SourceExists("ThreadException"))
                {
                    EventLog.CreateEventSource("ThreadException", "Application");
                }

                // Create an EventLog instance and assign its source.
                EventLog myLog = new EventLog();
                myLog.Source = "ThreadException";
                myLog.WriteEntry(errorMsg + ex.Message + "\n\nStack Trace:\n" + ex.StackTrace);
            }
            catch (Exception exc)
            {
                try
                {
                    MessageBox.Show(
                        "Fatal Non-UI Error. Could not write the error to the event log. Reason: " + exc.Message, 
                        "Fatal Non-UI Error" , 
                        MessageBoxButtons.OK, 
                        MessageBoxIcon.Stop);
                }
                finally
                {
                    Application.Exit();
                }
            }
        }

        // Handle the UI exceptions by showing a dialog box, and asking the user whether or not they wish to abort execution.
        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            DialogResult result = DialogResult.Cancel;
            try
            {
                result = ShowThreadExceptionDialog("Windows Forms Error", e.Exception);
            }
            catch
            {
                try
                {
                    MessageBox.Show("Fatal Windows Forms Error",
                        "Fatal Windows Forms Error", 
                        MessageBoxButtons.AbortRetryIgnore, 
                        MessageBoxIcon.Stop);
                }
                finally
                {
                    Application.Exit();
                }
            }

            // Exits the program when the user clicks Abort.
            if (result == DialogResult.Abort)
                Application.Exit();
        }


        // Creates the error message and displays it.
        private static DialogResult ShowThreadExceptionDialog(string title, Exception e)
        {
            string errorMsg = "An application error occurred. Please contact the adminstrator with the following information:\n\n";
                   errorMsg = errorMsg + e.Message + "\n\nStack Trace:\n" + e.StackTrace;
            return MessageBox.Show(errorMsg, title, MessageBoxButtons.AbortRetryIgnore,MessageBoxIcon.Stop);
        }
    }
}
ErrorHandlerForm Code
using System.Threading;

namespace GlobalErrorHandle
{
    public partial class ErrorHandlerForm : Form
    {
        public ErrorHandlerForm()
        {
            InitializeComponent();
        }

        // Programs the button to throw an exception when clicked.
        private void button1_Click(object sender, EventArgs e)
        {
            throw new ArgumentException("The parameter was invalid");
        }

        Thread newThread = null;
        // Start a new thread, separate from Windows Forms, that will throw an exception.
        private void button2_Click(object sender, EventArgs e)
        {
            ThreadStart newThreadStart = new ThreadStart(newThread_Execute);
            newThread = new Thread(newThreadStart);
            newThread.Start();
        }

        // The thread we start up to demonstrate non-UI exception handling. 
        void newThread_Execute()
        {
            throw new Exception("The method or operation is not implemented.");
        }
    }
}
點擊 button1 結果

[C#] Global Exception Handler-3

點擊 button2 結果(無權限寫入 Log)

[C#] Global Exception Handler-4

點擊 button2 結果(有權限寫入 Log),會出現下面畫面,並寫入 Log 內

[C#] Global Exception Handler-1

[C#] Global Exception Handler-2

[C#] Global Exception Handler-5

沒有留言:

張貼留言