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

スポンサーリンク

崩壊するアプリを「静かなる戦士」に変える:VB.NET例外処理の極致

業務自動化ツールを開発しているとき、最も恐ろしいのは「ユーザーの環境でだけ落ちる」現象だ。開発者のPCで完璧に動作しても、現場の権限設定や不明なデータ形式によってアプリは突如として息絶える。

多くの初心者は、全メソッドを `Try-Catch` で囲むという非効率な「泥沼」に陥る。だが、真のエンジニアは違う。「アプリケーションの境界線」で例外を統括管理するアーキテクチャを組むのだ。

今日は、Windows Formsにおいて、予期せぬクラッシュを「データ」へと昇華させ、アプリを死なせないための究極の設計論を授ける。

—

1. 例外の「二重防壁」:役割分担を理解せよ

VB.NETにおける例外管理には、2つの防壁が必要だ。この役割を混同してはならない。

  • `Application.ThreadException` (UIスレッドの守護者)
  • Windows Formsのメインスレッド(UI)で発生した未処理例外を捕捉する。
  • ここで例外を処理すれば、アプリを強制終了させずに「継続」させることが可能だ。
  • `AppDomain.CurrentDomain.UnhandledException` (プロセスの断末魔)
  • バックグラウンドスレッドや、CLR(共通言語ランタイム)レベルの致命的なエラーを捕捉する。
  • ここでは「継続」は不可能だ。ログを書き出し、ユーザーに丁寧に謝罪してプロセスを終了させるための「最後の砦」である。

—

2. 堅牢な例外ポリシーの実装例

以下のコードを `Program.vb` (または `Sub Main`) に組み込むことで、アプリは「落ちる」から「報告する」へと進化する。

Imports System.Threading
Imports System.Windows.Forms

Module Program

Sub Main()
‘ 1. UIスレッドの例外を捕捉
AddHandler Application.ThreadException, AddressOf Application_ThreadException

‘ 2. 全スレッドの未処理例外を捕捉(バックグラウンド処理用)
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf CurrentDomain_UnhandledException

Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New MainForm())
End Sub

‘ UIスレッドの例外処理:ここでアプリを継続させるか判断
Private Sub Application_ThreadException(sender As Object, e As ThreadExceptionEventArgs)
Dim ex As Exception = e.Exception
ReportError(ex, “UI Thread”)

MessageBox.Show(“予期せぬエラーが発生しました。作業内容を保存して再起動してください。”, “システムエラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Sub

‘ グローバル例外処理:プロセス終了直前の記録
Private Sub CurrentDomain_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(e.ExceptionObject, Exception)
ReportError(ex, “Global Exception”)

‘ 致命的なエラーなので、ユーザーに通知後、確実にプロセスを終了させる
Environment.FailFast(“致命的なクラッシュが発生しました。”)
End Sub

‘ ログ送信の共通ロジック
Private Sub ReportError(ex As Exception, source As String)
‘ ここでログファイル書き込みや、API経由でのサーバーへのスタックトレース送信を実装する
‘ 注意: ファイルアクセス失敗で無限ループにならないよう、IOは最小限に
Dim logMsg As String = $”[{DateTime.Now}] Source:{source}{vbCrLf}{ex.ToString()}”
System.IO.File.AppendAllText(“error_log.txt”, logMsg)
End Sub
End Module

—

3. 実務で「絶対」に守るべき3つの鉄則

① ログ出力の「再帰的例外」を避ける

ログ送信中にデータベース接続エラーが発生し、そのエラー処理でまたログを書こうとして…という無限ループは現場で最も多い悲劇だ。ログ出力処理は `Try-Catch` を一切使わないか、極めて原始的な `System.IO.File.AppendAllText` のような枯れたメソッドを使うこと。

② 環境情報を付与せよ

スタックトレースだけでは不十分だ。以下の情報を必ずログに含めろ。

  • `Environment.OSVersion`(OS環境)
  • `System.Security.Principal.WindowsIdentity.GetCurrent().Name`(実行ユーザー)
  • アプリのバージョン番号

③ UIの「フリーズ」と「クラッシュ」を分ける

`ThreadException` で例外を飲み込んで継続させるのは、「軽微なバグ」の場合のみだ。状態が整合性を失った(例:データベース接続が切れた状態)のに無理やり動かし続けると、データ破損を招く。「安全にシャットダウンするための再起動」をユーザーに促す設計が、真のプロフェッショナルだ。

—

最後に:なぜ「放置」が罪なのか

もしあなたが、例外を握りつぶすコードを書いていれば、それは「ユーザーの貴重な時間を奪う泥棒」と同じだ。ユーザーは、何が起きたか分からず、ただ再起動を繰り返す。

今回紹介した設計を導入すれば、あなたのアプリは「黙って落ちる無責任なツール」から「自らの問題を報告する賢明なソリューション」へと生まれ変わる。

コードを書くことは、ただ機能を実装することではない。「いかにしてシステムの寿命を延ばし、ユーザーの信頼を守り抜くか」という哲学の体現だ。今日から、その設計思想をコードに刻み込め。

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