Application.ThreadException イベント ハンドラーで例外のチェーン全体を取得するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/61354

  •  09-06-2019
  •  | 
  •  

質問

.NET 2.0 アプリの例外処理の修正に取り組んでいたところ、奇妙な問題に遭遇しました。 アプリケーション.スレッド例外.

私が望むのは、GUI 要素の背後にあるイベントからのすべての例外をキャッチできるようにすることです (例:ボタン_クリックなど)。次に、これらの例外を「致命的」に基づいてフィルタリングしたいと思います。例外の種類によっては、アプリケーションは実行を継続する必要がありますが、他の種類の例外では終了する必要があります。

別の .NET 2.0 アプリでは、デフォルトではデバッグ モードでのみ例外が実際に Application.Run または Application.DoEvents 呼び出しから出ることがわかりました。リリース モードではこれは発生せず、Application.ThreadException イベントを使用して例外を「キャッチ」する必要があります。

ところが今になって気づいたのですが、 Application.ThreadException イベントの ThreadExceptionEventArgs で渡される例外オブジェクトは、常に例外チェーンの最も内側の例外です。. 。ただし、ロギング/デバッグ/設計の目的では、例外のチェーン全体が本当に必要です。たとえば、SocketException を処理するときなど、どの外部システムが失敗したかを判断するのは簡単ではありません。たとえば次のようにラップされている場合NpgsqlException の場合、少なくともデータベースの問題であることがわかります。

では、このイベントから例外の連鎖全体に到達するにはどうすればよいでしょうか? それは可能でしょうか、それとも別の方法で例外処理を設計する必要がありますか?

私は - ある種 - を持っていることに注意してください 回避策 を使用して Application.SetUnhandledExceptionMode, ただし、独自のメッセージ ループを実行する必要があるため、これは理想とは程遠いです。

編集:これ以上の間違いを防ぐために、 GetBaseException() メソッドが必要な動作をしません:これは最も内側の例外を返すだけですが、私がすでに持っているのは最も内側の例外だけです。最外側の例外を取得したい!

役に立ちましたか?

解決

この質問は、ここでより分かりやすく表現され、回答されています。

内部例外が実際にスローされた例外ではなく、ThreadException ハンドラーに到達するのはなぜですか?

他のヒント

この動作を再現しようとしました(常に最も内側の例外が発生します)。
しかし、すべての 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 を作成した例外が返されます。その後、同じプロセスを使用してチェーンを上流に進み、すべての例外を取得できます。

getbaseException メソッド

このチェーンの情報の一部に基づいて、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

これはまさにあなたが探しているものだと思います。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top