WindowsFormアプリケーションです。
VBで「アプリケーションフレームワークを有効にする」をチェックONにしたときに、例外を一括処理する方法です。
C#ではアプリケーションのエントリーポインはMainメソッドになります。
補足されなかった例外を一括で処理する場合、このMainメソッド内で
System.Windows.Forms.Application.ThreadExceptionイベントと
System.AppDomain.CurrentDomain.UnhandledExceptionイベントをハンドルして処理すると思います。
VBでもエントリーポイントをMainメソッドにした場合、C#と同じ方法で補足されなかった例外を一括処理できます。
しかし、VBではデフォルトで「アプリケーションフレームワークを有効にする」チェックONなので
スタートアップフォームを指定してアプリケーションを起動することが多いように思います。
私に権限があれば自作のMainメソッドをエントリーポイントにしてアプリを起動するようにするのですが
(そのほうが細かいこともまで融通が利くので)
政治的な理由で「アプリケーションフレームワークを有効にする」チェックOFFにできない場合
アプリケーションイベントを使用して、補足されなかった例外を一括処理できます。
まずアプリケーションイベントとはVBにのみ用意されている、アプリケーションに関するイベントを記述することができるクラスです。
このクラスファイルはデフォルトでは非表示です。
まずはこのファイルを表示させます。
プロジェクトのプロパティ画面を開き「アプリケーション」タブにある「アプリケーションイベントの表示」をクリックします。
するとプロジェクト内に「ApplicationEvent.vb」ファイルが表示されます。
このファイルの中を見ると
名前空間は「My」クラス名は「MyApplication」になっています。
「Partial」キーワードが付いているので、どこかに分離されたコードがあります。
「My Project」の「Application.myapp」の「Application.Designer.vb」が分離されたコードになります。
MyApplicationクラスのコードのコメントに
「UnhandledException: ハンドルされていない例外がアプリケーションで発生したときに発生するイベントです。」とあるので
一見、これを使えばよさそうな気がします。
結論からいいますと、ダメでした。
例外の補足には
メインフォームが実行されているスレッド(UIスレッド)で発生する例外と
メインフォームが実行されているスレッド以外(UIスレッド以外)で発生する例外を補足する必要があります。
UIスレッドで発生する例外を補足するには
System.Windows.Forms.Application.ThreadExceptionイベントを使用します。
UIスレッド以外で発生する例外を補足するには
System.AppDomain.CurrentDomain.UnhandledExceptionイベントを使用します。
MyApplicationクラスのUnhandledExceptionイベントは、UIスレッドで発生する例外を補足できるようです。
UIスレッド以外で発生する例外を補足するために、StartUpイベントを使用して
System.AppDomain.CurrentDomain.UnhandledExceptionイベントをハンドルしてみました。
以下がダメな場合のコードです。
実行するとUIスレッドで発生した例外もすべて、CurrentDomain_UnhandledExceptionメソッドで処理されてしまいます。
ダメなコードです!!
Namespace My
' 次のイベントは MyApplication に対して利用できます:
'
' Startup: アプリケーションが開始されたとき、スタートアップ フォームが作成される前に発生します。
' Shutdown: アプリケーション フォームがすべて閉じられた後に発生します。このイベントは、通常の終了以外の方法でアプリケーションが終了されたときには発生しません。
' UnhandledException: ハンドルされていない例外がアプリケーションで発生したときに発生するイベントです。
' StartupNextInstance: 単一インスタンス アプリケーションが起動され、それが既にアクティブであるときに発生します。
' NetworkAvailabilityChanged: ネットワーク接続が接続されたとき、または切断されたときに発生します。
Partial Friend Class MyApplication
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf CurrentDomain_UnhandledException
End Sub
Private Sub MyApplication_UnhandledException(sender As Object, e As ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
'アプリケーションを終了しない
e.ExitApplication = False
'例外メッセージを表示
MessageBox.Show(e.Exception.Message,"MyApplication_UnhandledException")
End Sub
Private Sub CurrentDomain_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs)
'例外メッセージを表示
MessageBox.Show(DirectCast(e.ExceptionObject, Exception).Message,"CurrentDomain_UnhandledException")
End Sub
End Class
End Namespace
UIスレッド以外のスレッドは使用しないから、UnhandledExceptionイベントだけハンドルすればいいかと思ってたのですが
メインスレッドのコンストラクタ内で例外が発生すると、補足できません。
※コンストラクタで例外が発生するのがどうかと思うのですが、ごくたまにサードパーティ製のコントロールが例外をスローして
例外が補足できずに標準の例外が出てしまうことがありました。
OnInitializeメソッドをオーバーライドして
System.Windows.Forms.Application.ThreadExceptionイベントと
System.AppDomain.CurrentDomain.UnhandledExceptionイベントをハンドルすると、希望通りの動作になりました。
Namespace My
' 次のイベントは MyApplication に対して利用できます:
'
' Startup: アプリケーションが開始されたとき、スタートアップ フォームが作成される前に発生します。
' Shutdown: アプリケーション フォームがすべて閉じられた後に発生します。このイベントは、通常の終了以外の方法でアプリケーションが終了されたときには発生しません。
' UnhandledException: ハンドルされていない例外がアプリケーションで発生したときに発生するイベントです。
' StartupNextInstance: 単一インスタンス アプリケーションが起動され、それが既にアクティブであるときに発生します。
' NetworkAvailabilityChanged: ネットワーク接続が接続されたとき、または切断されたときに発生します。
Partial Friend Class MyApplication
'''
''' OnInitializeメソッドをオーバーライド
'''
'''
'''
'''
Protected Overrides Function OnInitialize(commandLineArgs As ObjectModel.ReadOnlyCollection(Of String)) As Boolean
'ハンドルされていない例外を補足する
'--UIスレッドでの例外を補足
AddHandler System.Windows.Forms.Application.ThreadException, AddressOf Application_ThreadException
'--UIスレッド以外での例外を補足
AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf CurrentDomain_UnhandledException
Return MyBase.OnInitialize(commandLineArgs)
End Function
'''
''' UIスレッドでの例外を補足するイベント
'''
'''
'''
'''
Private Sub Application_ThreadException(sender As Object, e As System.Threading.ThreadExceptionEventArgs)
'スプラッシュ画面を閉じる
CloseSplashScreen()
'例外処理を行う
ShowError(e.Exception)
End Sub
'''
''' UIスレッド以外での例外を補足するイベント
'''
'''
'''
'''
Private Sub CurrentDomain_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs)
'※UIスレッド以外のスレッドなので、UIスレッドの画面操作はできない
'-----
''スプラッシュ画面を閉じる
'CloseSplashScreen()
'-----
'例外処理を行う
ShowError(DirectCast(e.ExceptionObject, Exception))
End Sub
'''
''' スプラッシュ画面を閉じる
'''
'''
Private Sub CloseSplashScreen()
If (My.Application.SplashScreen IsNot Nothing AndAlso My.Application.SplashScreen.IsDisposed <> True) Then
My.Application.SplashScreen.Close()
End If
End Sub
'''
''' 例外処理を行う
'''
'''
'''
Private Sub ShowError(ex As Exception)
Dim info As New System.Text.StringBuilder
info.AppendLine(ex.Message)
info.AppendLine(ex.StackTrace)
info.AppendLine("------メソッド呼出履歴------")
For Each frm As StackFrame In New StackTrace(ex, True).GetFrames
Dim mb As System.Reflection.MethodBase = frm.GetMethod
info.AppendLine(mb.ReflectedType.FullName & " : " & mb.Name)
Next
MessageBox.Show(info.ToString)
End Sub
End Class
End Namespace
0 件のコメント:
コメントを投稿