「ほっ」と。キャンペーン
(VB.Net)ハンドルされない例外を捕まえる方法
本来はイベント毎にTry Catch構文で例外を捕まえたらいいのでしょうが、なかなかそうはいかないこともあります。

デバッグ中にハンドルされない例外が発生すると下記のようになります。
e0091163_18233286.jpg


デバッグ中でない場合に実行すると下記のようにエラーダイアログがでます。
e0091163_1824219.jpg


さて、.Netにはこのようにハンドルされない例外を捕まえる方法があります。
その方法には下記の2つがあるようでです。
1. Application.ThreadExceptionイベントの活用
2. Thread.GetDomain().UnhandledExceptionイベントの活用

Application.ThreadExceptionはメインスレッドのハンドルされていない例外のみ捕まえますが、Thread.GetDomain().UnhandledExceptionはメインスレッド以外のスレッドや、コンソールアプリケーションの例外も捕まえれるようです。

詳しくは参考もとの@IT 適切に処理されなかった例外をキャッチするには?捕捉されなかった例外がスローされたことを知るに載っています。

さて、これらのイベントハンドラをどこで定義するかですが、@ITの例だとVB.Netの場合 Sub Main() で定義しています。
Sub Main() というのは C とかのMain関数と同じで、VB.Netのエントリーポイント(プログラムの実行を開始する場所)のことっぽいですね。
ところが、VB2005からは アプリケーションフレームワーク たるものが導入されており、これが有効になっていると Sub Main() はコンパイル時に自動で生成されるため、開発者が勝手に Sub Main() を作れません。
(アプリケーションフレームワークの設定はプロジェクトのプロパティ→アプリケーションのところにあります。これがあると2重起動を簡単にやめさせることができます)
今回はアプリケーションフレームワークは欠かせせないのでこれを有効にしたままハンドルされない例外を捕まえるイベントハンドラを定義したかったので、スタートアップフォームのコンストラクタに書くことにしました。

下記がそのソースです。
ついでに、例外発生時は Windows のイベントビュアーにエラーイベントログとして書き出す処理も入れています。
Public Class スタートアップフォーム
 
Public Sub New()
' この呼び出しは、Windows フォーム デザイナで必要です。
InitializeComponent()
 
' InitializeComponent() 呼び出しの後で初期化を追加します。
 
' ThreadExceptionイベント・ハンドラを登録する
AddHandler Application.ThreadException, AddressOf Application_ThreadException
' UnhandledExceptionイベント・ハンドラを登録する
AddHandler System.Threading.Thread.GetDomain().UnhandledException, AddressOf Application_UnhandledException
End Sub
 
''' <summary>
''' 未処理例外をキャッチするイベントハンドラ。
''' メインスレッド用。(WindowsForm専用)
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Public Shared Sub Application_ThreadException(ByVal sender As Object, ByVal e As System.Threading.ThreadExceptionEventArgs)
'メッセージボックス表示
MessageBox.Show(e.Exception.Message)
'イベントログ出力
OutPutLogErr(e.Exception)
'予期せぬ例外時は強制終了
Environment.Exit(-1)
 
End Sub
 
''' <summary>
''' 未処理例外をキャッチするイベントハンドラ。
''' 別スレッド用。
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Public Shared Sub Application_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
Dim ex As Exception = CType(e.ExceptionObject, Exception)
If Not ex Is Nothing Then
'メッセージボックス表示
MessageBox.Show(ex.Message)
'イベントログ出力
OutPutLogErr(ex)
'予期せぬ例外時は強制終了
Environment.Exit(-1)
End If
End Sub
 
''' <summary>
''' エラーログをイベントビュアーに出力
''' </summary>
''' <param name="e">Exceptionオブジェクト</param>
''' <remarks></remarks>
Private Shared Sub OutPutLogErr(ByVal e As Exception)
Try
'ソース
Dim sourceName As String = "テストアプリケーション"
'ソースが存在していない時は、作成する
If Not System.Diagnostics.EventLog.SourceExists(sourceName) Then
'ログ名を空白にすると、"Application"となる
System.Diagnostics.EventLog.CreateEventSource(sourceName, "")
End If
'テスト用にイベントログエントリに付加するデータを適当に作る
Dim myData() As Byte = {}
Dim msg As String = "例外:" & vbNewLine & e.Message & vbNewLine & "例外スタックトレース" & vbNewLine & e.StackTrace & vbNewLine
If e.InnerException IsNot Nothing Then
msg = msg & "InnerException:" & vbNewLine & e.InnerException.Message & vbNewLine & "InnerExceptionスタックトレース:" & vbNewLine & e.InnerException.StackTrace
End If
'イベントログにエントリを書き込む
'ここではエントリの種類をエラー、イベントIDを1、分類を1000とする
System.Diagnostics.EventLog.WriteEntry(sourceName, msg, System.Diagnostics.EventLogEntryType.Error, 1, 1000, myData)
Catch ex As Exception
End Try
End Sub
End Class

ちなみに、Visual Studioで普通にデバッグ開始すると、最初のSSのように、例外が起こったソースの箇所で処理が止まります。
なので、Application.ThreadExceptionイベント、Thread.GetDomain().UnhandledExceptionイベントを捕まえるテストするには、Viual Studioのメニューでデバッグ→デバッグなしで開始を選択するとできます。

イベントログには下記のように出力されます。
e0091163_18242935.jpg


参考:
アプリケーションが始まるときに呼び出す位置(エントリポイント)を変更する
イベントログにエントリを書き込む
[PR]
by jehoshaphat | 2008-12-20 18:33 | .Net開発 | Trackback | Comments(0)
トラックバックURL : http://jehupc.exblog.jp/tb/9247467
トラックバックする(会員専用) [ヘルプ]
※このブログはトラックバック承認制を適用しています。 ブログの持ち主が承認するまでトラックバックは表示されません。


<< (VB.Net)RichTex... (.Net)Control.R... >>