例外の「魂」を殺すな:ExceptionDispatchInfoによるスタックトレース完全保存の極意
VBAからVB.NETへの移行、あるいはレガシーなWindows Formsアプリケーションの保守において、最も多くのエンジニアが犯す致命的なミスがある。それは「例外を握りつぶす」、あるいは「再スローによってスタックトレースを破壊する」という罪だ。
`Catch`ブロックで`Throw ex`と書いていないか? もしそうなら、君はデバッグという名の暗闇の中で、最も重要な「どこで死んだか」という証拠を自ら抹消していることになる。
今日は、非同期処理やスレッド間通信において、例外の正当性を保ちながらメインスレッドへ伝播させる、極限のテクニックを解説する。
—
1. なぜ「Throw ex」は禁忌なのか
VB.NETの`Catch ex As Exception`内で`Throw ex`を実行すると、CLRは新しい例外としてスタックトレースを再構築する。これにより、本来の発生源(メソッドの深い階層)が隠蔽され、スタックトレースの最上部には「再スローした場所」しか残らない。
特にマルチスレッド環境では、例外は発生元スレッドで捕獲され、それをメインスレッドへ運ぶ必要がある。この際、単に例外オブジェクトを渡すだけでは不十分だ。
2. ExceptionDispatchInfoの真価
`System.Runtime.ExceptionServices.ExceptionDispatchInfo`(以下EDI)は、まさにこのために存在する。これは例外の「状態(スタックトレースを含む)」をキャプチャし、任意のタイミングで、あたかもその場所で発生したかのように例外を再スローできる強力なツールだ。
実践:スレッド間例外の完全移送
以下のコードは、バックグラウンドスレッドで発生した例外を捕獲し、UIスレッド(またはメインスレッド)へ正確に持ち帰るためのテンプレートである。
Imports System.Runtime.ExceptionServices
Imports System.Threading.Tasks
Public Class ExceptionHandler
‘ バックグラウンドの処理を想定
Public Sub ExecuteComplexTask()
Dim capturedException As ExceptionDispatchInfo = Nothing
Try
‘ Windows API呼び出しや重いデータ処理
Throw New InvalidOperationException(“メモリ整合性の問題が発生”)
Catch ex As Exception
‘ 例外の状態をキャプチャ(これがスタックトレースの魂を保存する)
capturedException = ExceptionDispatchInfo.Capture(ex)
End Try
‘ メインスレッドへ戻る(InvokeやDispatcherを利用)
‘ ここでは簡略化のため、別スレッドで再スローするシナリオ
If capturedException IsNot Nothing Then
‘ 任意のタイミングで再スロー。元のスタックトレースは維持される
capturedException.Throw()
End If
End Sub
End Class
—
3. レガシー環境とメモリ最適化への配慮
シニアエンジニアとして忘れてはならないのは、例外処理がメモリ管理と密接に関係している点だ。
- IDisposableの徹底: 非同期処理内で生成したアンマネージド・リソース(`SafeHandle`派生オブジェクトやファイルハンドル)は、例外発生時に必ず解放しなければならない。`Try…Finally`ブロックは、例外が捕捉される前であっても必ず実行されることを利用し、明示的に`Dispose()`を呼ぶこと。
- Finalizerの過信を捨てる: レガシーシステムでメモリ不足(OOM)に悩む場合、GCの自動解放を待つのは愚策だ。VB.NETでは`Using`構文を活用し、スコープを最小化することで、スタック上の参照を速やかに断ち切ることが、GCの負荷を劇的に下げる。
4. チーフアーキテクトからの提言
システム間連携において、遠隔の例外をローカルで再構築するEDIの技術は、もはや「高級テクニック」ではない。「必須の作法」である。
特に、Windows APIをP/Invokeで呼び出す際、`Marshal.GetLastWin32Error()`の結果と例外情報をセットでキャプチャしておく設計にすべきだ。エラーコードとスタックトレース、この二つが揃って初めて、夜中に呼び出される障害対応の地獄から君を救うことができる。
最後に
コードとは、書いた時点では未完成だ。運用という過酷な環境に晒され、例外という名の「悲鳴」を上げたとき、その悲鳴を歪めずに伝えることこそが、プロフェッショナルな設計者の務めである。
この`ExceptionDispatchInfo`を使いこなし、君の書くシステムを「透明性の高い、堅牢な城」へと昇華させてほしい。
—
追伸:もし君が依然として `On Error Resume Next` の幻影を追っているなら、今すぐそのコードを削除することを推奨する。技術は進化する。それに追いつけない者は、ただの遺物になるだけだ。
