【テクニカル・上級編】VB.NETにおける例外ポリシーの設計:Application.ThreadExceptionとAppDomain.UnhandledExceptionの役割分担とクラッシュレポート自動送信 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

墓場まで持っていく例外設計:VB.NETにおける「不沈艦」アプリケーションの構築術

VB.NETを扱うエンジニアにとって、アプリケーションが突如として「真っ白なダイアログ」と共に強制終了するのは、職務怠慢に等しい。特に現場のレガシーなWindows Formsアプリケーションでは、リソース管理の甘さがメモリリークを招き、それが例外の温床となる。

今日は、ユーザーにエラーを見せず、かつ開発者がデバッグに必要な情報を完璧に収集し、アプリを自律的に復旧させるための「例外制御の極致」を伝授する。

—

1. 例外の「二層構造」を理解せよ

VB.NETの例外処理には、明確な役割分担がある。多くの初心者はこれを混同し、ログを取り逃がす。

  • `Application.ThreadException`: UIスレッド(メインスレッド)で発生した例外を捕捉する。WinFormsのイベントハンドラ内で投げられたエラーはここを通る。
  • `AppDomain.CurrentDomain.UnhandledException`: スレッドプール内の非同期処理や、メインスレッド以外で発生した「致命的(Fatal)」な例外を捕捉する。

これら二つを適切にハンドリングしなければ、アプリケーションの「死角」を放置していることと同義だ。

—

2. 実装の核心:クラッシュレポート・ハンドラの構築

以下のコードは、単なるログ出力ではない。メモリの解放を意識し、OSの負荷を最小限に抑えつつ情報を収集する構成だ。

Imports System.Threading
Imports System.IO

Friend NotInheritable Class GlobalExceptionHandler

‘ アプリケーション開始時に一度だけ呼び出す
Public Shared Sub RegisterHandlers()
‘ UIスレッドの例外
AddHandler Application.ThreadException, AddressOf UIThreadExceptionHandler
‘ 非同期/バックグラウンドの例外
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf UnhandledExceptionHandler
End Sub

Private Shared Sub UIThreadExceptionHandler(sender As Object, e As ThreadExceptionEventArgs)
ProcessException(e.Exception, “UIThread”)
End Sub

Private Shared Sub UnhandledExceptionHandler(sender As Object, e As UnhandledExceptionEventArgs)
ProcessException(DirectCast(e.ExceptionObject, Exception), “NonUIThread”)
End Sub

Private Shared Sub ProcessException(ex As Exception, source As String)
‘ 1. スタックトレースと環境情報を構築
Dim report As String = $”Source: {source}{vbCrLf}Trace: {ex.StackTrace}{vbCrLf}Message: {ex.Message}”

‘ 2. ここでサーバーへの送信処理(HTTP POSTなど)を実装する
‘ 注意: 同期的に送るとアプリがフリーズするため、タスクか別プロセスで送るのが鉄則
SendReportToServer(report)

‘ 3. メモリの安全な解放を促す(GCの強制収集は慎重に)
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced)

‘ 4. ユーザーへ通知し、必要なら再起動する
MessageBox.Show(“予期せぬエラーが発生しました。ログを送信しました。”, “System Error”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Application.Restart()
Environment.Exit(0)
End Sub
End Class

—

3. シニアエンジニアが守るべき「3つの鉄則」

① メモリ管理は「明示的」に行え

VB.NETはガベージコレクション(GC)があるが、Windows FormsのCOMオブジェクトやグラフィックス・リソース(GDI+)は別物だ。`IDisposable`を実装しているオブジェクトは、例外発生時であっても確実に`Dispose()`を呼ぶ設計にしなければならない。`Using`構文を怠ることは、設計の怠慢である。

② コンテキストの保存

例外レポートには、以下の情報を必ず含めよ。これがないレポートはただのゴミである。

  • OSのビルド番号: `Environment.OSVersion`
  • メモリ使用量: `Process.GetCurrentProcess().PrivateMemorySize64`
  • 実行モジュールのパス: アプリの更新状況を特定するため

③ 再起動の罠を回避せよ

`Application.Restart()`は強力だが、デッドロックやリソースの解放待ちが発生している場合、再起動自体が失敗する。必ずプロセス終了前に`Process.GetCurrentProcess().Kill()`に近い制御を検討すること。

—

4. 伝説的な現場の結論

多くの企業でVBAからVB.NETへの移行を指揮してきたが、結局のところ、優れたシステムとは「例外が起きないシステム」ではなく、「例外が起きた後の振る舞いが美しいシステム」のことだ。

エラーダイアログをユーザーに見せるのは敗北である。背後で静かに情報を吸い上げ、何事もなかったかのように再起動し、裏側で開発者が次のリリースまでに確実に修正できる体制を作ることこそが、エンジニアとしての真の矜持である。

この設計を導入した瞬間から、あなたのアプリケーションは「不安定なパッチワーク」から「堅牢なエンタープライズ製品」へと進化するはずだ。健闘を祈る。

タイトルとURLをコピーしました