業務アプリの「最後の砦」を築く:VB.NETにおけるグローバル例外ハンドリングの真髄
こんにちは。現場で叩き上げられたVB.NETエンジニアの皆さん、あるいはVBAの世界から一歩踏み出し、堅牢なデスクトップアプリ構築を目指している皆さん。
業務アプリケーションを開発していると、必ずぶつかる壁があります。それは「想定外のエラー」です。どれだけ丁寧に `Try…Catch` を書いても、ネットワークの瞬断、OSの気まぐれ、あるいは未知のメモリリークが引き起こすクラッシュを100%防ぐことは不可能です。
「ソフトが突然消えた」「何をすればいいか分からない」というユーザーからの悲鳴を、ログと適切なフィードバックに変えるための「グローバル例外捕捉基盤」。これこそが、プロとアマを分かつ境界線です。今日は、その最終防衛ラインの構築法を伝授します。
—
1. なぜ「個別のTry…Catch」だけでは足りないのか?
VB.NETで開発を始めたばかりの頃は、全てのメソッドを `Try…Catch` で囲いたくなるかもしれません。しかし、それは「モグラ叩き」と同じです。
- 開発者の盲点: 開発者が予期しなかった場所で発生するエラーは、Catchできません。
- 非同期処理の闇: `Async/Await` を使った非同期処理の中で発生した例外は、通常の `Catch` をすり抜けてアプリケーション全体をクラッシュさせることがあります。
ここで登場するのが、.NETが提供する「2つの最終防衛ライン」です。
—
2. 2つの防衛ラインを理解する
VB.NETのデスクトップアプリ(Windows Forms等)には、主に2つの「捕獲場所」があります。
1. `Application.ThreadException`: UIスレッド(画面操作を行っているスレッド)で発生した例外をキャッチします。
2. `AppDomain.CurrentDomain.UnhandledException`: スレッドを問わず、プロセス全体で発生した「誰も拾わなかった例外」をキャッチします。
重要なポイント: この2つを両方実装することで、どんな場所で発生したエラーも漏らさない「網」を張ることができます。
—
3. 実践:最強の例外捕捉コード
それでは、実際に `Program.vb` (あるいは `Sub Main`) に組み込む実装例を見ていきましょう。
Imports System
Imports System.Windows.Forms
Imports System.Threading
Module Program
Sub Main()
‘ 1. UIスレッドの例外捕捉(Windows Forms用)
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)
ShowErrorMessage(“UIスレッドで予期せぬエラーが発生しました。”, e.Exception)
End Sub
‘ それ以外のスレッドで発生した例外を処理
Private Sub CurrentDomain_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(e.ExceptionObject, Exception)
ShowErrorMessage(“深刻なエラーが発生しました。アプリを終了します。”, ex)
End Sub
‘ ユーザーへの通知とログ記録の共通メソッド
Private Sub ShowErrorMessage(message As String, ex As Exception)
‘ ここでログファイルへの書き出しを行うのが定石です
‘ LogHelper.Write(ex.ToString())
MessageBox.Show(message & vbCrLf & vbCrLf & “詳細: ” & ex.Message,
“エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
‘ 終了処理が必要であればここで行う
Environment.Exit(1)
End Sub
End Module
—
4. 現場で役立つ「極意」
コードを書くだけで満足してはいけません。実務で使う際には、以下の3点を必ず意識してください。
① ログには「例外の型」と「スタックトレース」を必ず入れる
単にエラーメッセージを表示するだけでは、デバッグは不可能です。`ex.ToString()` を使えば、例外の種類、メッセージ、そしてどこで発生したかのスタックトレースまで一括で取得できます。これをテキストファイルに吐き出すだけで、解析時間は劇的に短縮されます。
② ユーザーをパニックにさせない
エラーメッセージには、専門用語を並べないこと。「NullReferenceExceptionが発生しました」と言われてもユーザーは困ります。「データの読み込みに失敗しました。お手数ですが、保存して再起動してください」といった、次にどうすべきかを伝えるメッセージを心がけましょう。
③ 開発環境ではあえて「捕捉しない」
デバッグ中は、`Try…Catch` で握りつぶしてしまうと、どこでバグが起きたのか分かりづらくなります。開発時はこれらのハンドラを無効にするか、あるいは「デバッガがアタッチされている時は処理を飛ばす」という分岐を入れるのがプロの作法です。
—
最後に:ここをクリアすれば、あなたは一人前です
例外処理を制する者は、システムを制します。
最初は「こんなコード、本当に必要?」と思うかもしれません。しかし、一度でもユーザーのPCで謎のクラッシュが起きた時、手元に精緻なログファイルがあれば、あなたはヒーローになれます。
VB.NETの基本は、単なる文法ではありません。「どうすればシステムが壊れないか」「壊れた時にどうリカバリーするか」という設計思想そのものです。
この「最終防衛ライン」をあなたのアプリに組み込んでみてください。それだけで、あなたの書くプログラムの品格が、数段上のレベルに引き上げられますよ。
何か分からないことがあれば、いつでも聞いてくださいね。応援しています。
