[C#][VB.NET]自定義例外對話框
之前在『例外處理使用時機』這篇有提到我目前很少會寫例外處理。除了在那篇提到的原因之外,還有個因素就是我會弄個自定義的例外處理視窗,讓使用者在例外發生時,可以匯出例外訊息並提供給開發人員。有了匯出的例外訊息,我們就可以很快的把未處理完的例外(指給程式員看的例外)給修正。
要做到自定義的例外對話框,我們需要利用Application.ThreadException事件。
先讓我們看一下MSDN的說明:
從MSDN的說明可以很清楚的看到,當執行緒發生例外,且該例外未被處理,則該事件即會觸發。
接著就讓我們來看看如何才能利用該事件做出自定義的例外對話框。首先,我們需要設計自定義的例外對話框。
接著撰寫繫上事件用的副程式。
VB.NET
Public Shared Sub ShowBugWindowOnError()
AddHandler Application.ThreadException, AddressOf OnErrorOccur
End Sub
C#
public static void ShowBugWindowOnError()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(OnErrorOccur);
}
最後寫上事件觸發時要執行的事件處理函式。
VB.NET
Protected Shared Sub OnErrorOccur(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
Dim errorDlg As New ExceptionDialog
errorDlg.DetailErrorMsg_TextBox.Text = GetDetailErrorMsg(e.Exception)
errorDlg.ShowDialog()
End Sub
C#
protected static void OnErrorOccur(object sender, System.Threading.ThreadExceptionEventArgs e)
{
ExceptionDlg errorDlg = new ExceptionDlg();
errorDlg.DetailErrorMsg_TextBox.Text = GetDetailErrorMsg(e.Exception);
errorDlg.ShowDialog();
}
到此一個簡單的自定義例外對話框就完成了。使用上,我們只需呼叫剛寫的繫上事件用副程式即可。
VB.NET
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ExceptionDialog.ShowBugWindowOnError()
End Sub
C#
private void Form1_Load(object sender, EventArgs e)
{
ExceptionDlg.ShowBugWindowOnError();
}
完整程式碼:
VB.NET
Imports System.Windows.Forms
Imports System.Threading
Imports System.Text
Public Class ExceptionDialog
#Region "Const"
Const OpenDetailButtonText As String = "v 詳細資料"
Const CloseDetailButtonText As String = "^ 詳細資料"
#End Region
#Region "Var"
Private _isDetailOpened As Boolean
#End Region
#Region "Public Shared Method"
'***************************************************************************
'Author: Larry Nung
'Date: 2009/4/9
'Purpose:
'Memo:
'***************************************************************************
''' <summary>
''' Shows the bug window on error.
''' </summary>
''' <remarks></remarks>
Public Shared Sub ShowBugWindowOnError()
AddHandler Application.ThreadException, AddressOf OnErrorOccur
End Sub
#End Region
#Region "Protected Shared Method"
'***************************************************************************
'Author: Larry Nung
'Date: 2009/4/9
'Purpose:
'Memo:
'***************************************************************************
''' <summary>
''' Called when [error occur].
''' </summary>
''' <param name="sender">The sender.</param>
''' <param name="e">The <see cref="System.Threading.ThreadExceptionEventArgs" /> instance containing the event data.</param>
''' <remarks></remarks>
Protected Shared Sub OnErrorOccur(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
Dim errorDlg As New ExceptionDialog
errorDlg.DetailErrorMsg_TextBox.Text = GetDetailErrorMsg(e.Exception)
errorDlg.ShowDialog()
End Sub
#End Region
#Region "Private Shared Method"
'***************************************************************************
'Author: Larry Nung
'Date: 2009/4/9
'Purpose:
'Memo:
'***************************************************************************
''' <summary>
''' Gets the detail error MSG.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function GetDetailErrorMsg(ByVal e As Exception) As String
Dim str As New StringBuilder
str.AppendLine(String.Format("Source: {0}", e.Source))
str.AppendLine(String.Format("Message: {0}", e.Message))
str.AppendLine(String.Format("TargetSite: {0}", e.TargetSite))
str.AppendLine("")
str.AppendLine("StackTrace: ")
str.AppendLine(e.StackTrace)
Return str.ToString
End Function
#End Region
#Region "Event Process"
Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK_Button.Click
Me.DialogResult = System.Windows.Forms.DialogResult.OK
Me.Close()
End Sub
Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel_Button.Click
Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
Application.Exit()
End Sub
Private Sub Detail_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Detail_Button.Click
_isDetailOpened = Not _isDetailOpened
Detail_Button.Text = If(_isDetailOpened, CloseDetailButtonText, OpenDetailButtonText)
Me.Height = If(_isDetailOpened, Me.DetailErrorMsg_TextBox.Bottom, Me.DetailErrorMsg_TextBox.Top) + 32
End Sub
Private Sub ExceptionDialog_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Height = Me.DetailErrorMsg_TextBox.Top + 32
Me.ErrorIcon_Label.Image = SystemIcons.Error.ToBitmap
End Sub
#End Region
End Class
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ExceptionDlgTest
{
public partial class ExceptionDlg : Form
{
#region Const
const string OpenDetailButtonText = "v 詳細資料";
const string CloseDetailButtonText = "^ 詳細資料";
#endregion
#region Var
private Boolean _isDetailOpened;
#endregion
#region Construction
public ExceptionDlg()
{
InitializeComponent();
}
#endregion
#region Public Shared Method
//***************************************************************************
//Author: Larry Nung
//Date: 2009/4/9
//Purpose:
//Memo:
//***************************************************************************
/// <summary>
///
/// </summary>
public static void ShowBugWindowOnError()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(OnErrorOccur);
}
#endregion
#region Protected Shared Method
protected static void OnErrorOccur(object sender, System.Threading.ThreadExceptionEventArgs e)
{
ExceptionDlg errorDlg = new ExceptionDlg();
errorDlg.DetailErrorMsg_TextBox.Text = GetDetailErrorMsg(e.Exception);
errorDlg.ShowDialog();
}
#endregion
#region Private Shared Method
private static string GetDetailErrorMsg(Exception e)
{
StringBuilder str = new StringBuilder();
str.AppendLine(String.Format("Source: {0}", e.Source));
str.AppendLine(String.Format("Message: {0}", e.Message));
str.AppendLine(String.Format("TargetSite: {0}", e.TargetSite));
str.AppendLine("");
str.AppendLine("StackTrace: ");
str.AppendLine(e.StackTrace);
return str.ToString();
}
#endregion
#region Event Process
private void Cancel_Button_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void OK_Button_Click(object sender, EventArgs e)
{
this.Close();
}
private void Detail_Button_Click(object sender, EventArgs e)
{
_isDetailOpened = !_isDetailOpened;
Detail_Button.Text = _isDetailOpened ? CloseDetailButtonText : OpenDetailButtonText;
this.Height = _isDetailOpened ? this.DetailErrorMsg_TextBox.Bottom : this.DetailErrorMsg_TextBox.Top + 32;
}
private void ExceptionDlg_Load(object sender, EventArgs e)
{
this.Height = this.DetailErrorMsg_TextBox.Top + 32;
this.ErrorIcon_Label.Image = SystemIcons.Error.ToBitmap();
}
#endregion
}
}
執行畫面如下:
Conclusion
透過Application.ThreadException事件,我們可以很容易達到該篇的效果。但需注意這樣的作法只對繫上的執行緒有效,非繫上的執行緒若發生例外,將無法截取到。在實際應用上,也可依自己需求加入記錄到事件記錄簿等功能,甚至可以使用.NET預設的例外視窗來顯示,只是多加了一些自己的處理,讓使用者感覺不出差異。
AddHandler Application.ThreadException, AddressOf Application_ThreadException
...
End Sub
Private Sub Application_ThreadException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
...
Dim exceptionDlg As New ThreadExceptionDialog(e.Exception)
exceptionDlg.ShowDialog()
End Sub
另外,若有興趣的也可以改繫上AppDomain.UnhandledException試看看。