Application.ThreadException イベント ハンドラーで例外のチェーン全体を取得するにはどうすればよいですか?
質問
.NET 2.0 アプリの例外処理の修正に取り組んでいたところ、奇妙な問題に遭遇しました。 アプリケーション.スレッド例外.
私が望むのは、GUI 要素の背後にあるイベントからのすべての例外をキャッチできるようにすることです (例:ボタン_クリックなど)。次に、これらの例外を「致命的」に基づいてフィルタリングしたいと思います。例外の種類によっては、アプリケーションは実行を継続する必要がありますが、他の種類の例外では終了する必要があります。
別の .NET 2.0 アプリでは、デフォルトではデバッグ モードでのみ例外が実際に Application.Run または Application.DoEvents 呼び出しから出ることがわかりました。リリース モードではこれは発生せず、Application.ThreadException イベントを使用して例外を「キャッチ」する必要があります。
ところが今になって気づいたのですが、 Application.ThreadException イベントの ThreadExceptionEventArgs で渡される例外オブジェクトは、常に例外チェーンの最も内側の例外です。. 。ただし、ロギング/デバッグ/設計の目的では、例外のチェーン全体が本当に必要です。たとえば、SocketException を処理するときなど、どの外部システムが失敗したかを判断するのは簡単ではありません。たとえば次のようにラップされている場合NpgsqlException の場合、少なくともデータベースの問題であることがわかります。
では、このイベントから例外の連鎖全体に到達するにはどうすればよいでしょうか? それは可能でしょうか、それとも別の方法で例外処理を設計する必要がありますか?
私は - ある種 - を持っていることに注意してください 回避策 を使用して Application.SetUnhandledExceptionMode, ただし、独自のメッセージ ループを実行する必要があるため、これは理想とは程遠いです。
編集:これ以上の間違いを防ぐために、 GetBaseException() メソッドが必要な動作をしません:これは最も内側の例外を返すだけですが、私がすでに持っているのは最も内側の例外だけです。最外側の例外を取得したい!
解決
この質問は、ここでより分かりやすく表現され、回答されています。
他のヒント
この動作を再現しようとしました(常に最も内側の例外が発生します)。
しかし、すべての InnerException がそのままの状態で、期待した例外が発生します。
テストに使用したコードは次のとおりです。
Private Shared Sub Test1()
Try
Test2()
Catch ex As Exception
Application.OnThreadException(New ApplicationException("test1", ex))
End Try
End Sub
Private Shared Sub Test2()
Try
Test3()
Catch ex As Exception
Throw New ApplicationException("test2", ex)
End Try
End Sub
Private Shared Sub Test3()
Throw New ApplicationException("blabla")
End Sub
Private Shared Sub HandleAppException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
...
End Sub
Sub HandleAppException は Application.ThreadException を処理します。Test1() メソッドが最初に呼び出されます。
これは、HandleAppException で得られる結果 (ThreadExceptionEventArgs として) です。
ThreadException http://mediasensation.be/dump/?download=ThreadException.jpg
例外をキャッチして (再) スローするだけの場合、InnerExceptions は表示されませんが、Exception に追加されます。スタックトレース, 、 このような:
Test.vb の SO.Test3(): 166 行目
Test.vb の SO.Test2(): 159 行目
Test.vb の SO.Test1(): 151 行目
通常、例外が他のスレッドで発生した場合、Application.ThreadException 例外ハンドラーの基本例外を除く例外チェーン全体が失われるだけです。
から MSDN ライブラリ:
このイベントにより、Windows フォーム それ以外の場合は処理するアプリケーション で発生する未処理の例外 Windows フォーム スレッド。あなたの イベントハンドラを ThreadException に イベントを使用してこれらの例外を処理し、 これにより、アプリケーションは 不明な状態です。可能な場合は、 例外は、 構造化例外処理ブロック。
解決:スレッド化を行う場合は、すべてのスレッド/非同期呼び出しが try/catch ブロック内にあることを確認してください。または、あなたが言ったように、Application.SetUnhandledExceptionMode で遊ぶこともできます。
面白いものを発見しました。GUI イベントが異なれば、異なる結果が得られます。Form.Shown イベント ハンドラーから例外がスローされると、Application.ThreadException が最も内側の例外をキャッチしますが、まったく同じコードを Form.Load イベントで実行すると、 一番外側 Application.ThreadException で例外がキャッチされました。
Exception.GetBaseException メソッドを試してみましたか?これにより、Application.TreadException を作成した例外が返されます。その後、同じプロセスを使用してチェーンを上流に進み、すべての例外を取得できます。
このチェーンの情報の一部に基づいて、UnhandledExceptionMode.CatchException ではなく UnhandledExceptionMode.ThrowException を使用しました。次に、フォームの Run() の外側で例外をキャッチすると、例外のチェーン全体が得られます。
MSDN ドキュメントによると、次のようになります。
派生クラスでオーバーライドされると、後続の 1 つ以上の例外の根本原因である Exception を返します。
Public Overridable Function GetBaseException() As Exception
Dim innerException As Exception = Me.InnerException
Dim exception2 As Exception = Me
Do While (Not innerException Is Nothing)
exception2 = innerException
innerException = innerException.InnerException
Loop
Return exception2
End Function
これのバリエーションを使用して、例外チェーンを解析できます。
Public Sub LogExceptionChain(ByVal CurrentException As Exception)
Dim innerException As Exception = CurrentException.InnerException
Dim exception2 As Exception = CurrentException
Debug.Print(exception2.Message) 'Log the Exception
Do While (Not innerException Is Nothing)
exception2 = innerException
Debug.Print(exception2.Message) 'Log the Exception
'Move to the next exception
innerException = innerException.InnerException
Loop
End Sub
これはまさにあなたが探しているものだと思います。