ここでは、Windowsフォームアプリケーションとコンソールアプリケーションにおいて、Try...Catch...で捕捉(キャッチ、ハンドル、トラップ)されなかった例外(エラー)がスローされたときに、その例外の情報を知るための方法を紹介します。
ただし、できるだけエラー処理はTry...Catch...(詳しくは、「エラー処理(例外処理)の基本」)を使って行うべきであり、ここで紹介している方法はどうしてもそうしなければならない時のみ使用してください。
Windowsフォームアプリケーションでは、捕捉されなかった例外がスローされるとApplication.ThreadExceptionイベントが発生します。
ThreadExceptionイベントが発生するのは、Windowsフォームが作成、所有しているスレッド(UIスレッド)で例外がスローされた時だけです。例えば、Thread.Startメソッドや、デリゲートのBeginInvokeメソッドなどで開始されたスレッドで発生した例外では発生しません。
ThreadExceptionイベントハンドラが呼び出された時に何もしないと、アプリケーションは続行され、例外は無視されます。例外を無視したままアプリケーションを続行させるのは危険ですので、通常はThreadExceptionイベントハンドラにアプリケーションを終了させるコードを記述します。
以下に例を示します。この例ではエントリポイントへの記述がありますが、エントリポイントが分からないという場合は、「アプリケーションのエントリポイントを自作する」をご覧ください。
'Imports System.Windows.Forms ''' <summary> ''' アプリケーションのメイン エントリ ポイントです。 ''' </summary> <STAThread> _ Public Shared Sub Main() 'ThreadExceptionイベントハンドラを追加 AddHandler Application.ThreadException, _ AddressOf Application_ThreadException Application.Run(New Form1()) End Sub 'ThreadExceptionイベントハンドラ Private Shared Sub Application_ThreadException(sender As Object, _ e As System.Threading.ThreadExceptionEventArgs) Try 'エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー") Finally 'アプリケーションを終了する Application.Exit() End Try End Sub
//using System.Windows.Forms; /// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] private static void Main() { //ThreadExceptionイベントハンドラを追加 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler( Application_ThreadException); Application.Run(new Form1()); } //ThreadExceptionイベントハンドラ private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { try { //エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー"); } finally { //アプリケーションを終了する Application.Exit(); } }
補足:ThreadExceptionイベントハンドラでは、通常、次のような処理を行います。
このコードが書かれた状態で、例えばForm1クラスで以下のように例外をスローすると、メッセージボックスにメッセージ(「エラーのテストです。」)が表示されて、アプリケーションが終了します。
'Button1のClickイベントハンドラ Private Sub Button1_Click(sender As Object, e As EventArgs) _ Handles Button1.Click Throw New Exception("エラーのテストです。") End Sub
//Button1のClickイベントハンドラ private void Button1_Click(object sender, EventArgs e) { throw new Exception("エラーのテストです。"); }
デフォルトでは、ThreadExceptionイベントが発生するかどうかはアプリケーション構成ファイルの設定によります。アプリケーション構成ファイルの設定に関係なく、常にThreadExceptionイベントが発生されるようにするには、Application.SetUnhandledExceptionModeメソッドにUnhandledExceptionMode.CatchExceptionを指定します。逆に、常にThreadExceptionイベントが発生しないようにするには、UnhandledExceptionMode.ThrowExceptionを指定します。
SetUnhandledExceptionModeメソッドは、Application.Runの前に呼び出す必要があります。
'Imports System.Windows.Forms ''' <summary> ''' アプリケーションのメイン エントリ ポイントです。 ''' </summary> <STAThread> _ Public Shared Sub Main() 'ThreadExceptionイベントハンドラを追加 AddHandler Application.ThreadException, _ AddressOf Application_ThreadException '常にThreadExceptionが発生されるようにする Application.SetUnhandledExceptionMode( _ UnhandledExceptionMode.CatchException) Application.Run(New Form1()) End Sub 'ThreadExceptionイベントハンドラ Private Shared Sub Application_ThreadException(sender As Object, _ e As System.Threading.ThreadExceptionEventArgs) Try 'エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー") Finally 'アプリケーションを終了する Application.Exit() End Try End Sub
//using System.Windows.Forms; /// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] private static void Main() { //ThreadExceptionイベントハンドラを追加 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler( Application_ThreadException); //常にThreadExceptionが発生されるようにする Application.SetUnhandledExceptionMode( UnhandledExceptionMode.CatchException); Application.Run(new Form1()); } //ThreadExceptionイベントハンドラ private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { try { //エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー"); } finally { //アプリケーションを終了する Application.Exit(); } }
補足:SetUnhandledExceptionModeメソッドは、2番目のパラメータにFalseを指定することにより、アプリケーション例外モードとすることができるようです(デフォルトは、スレッド例外モード)。しかし現在は、SetUnhandledExceptionModeメソッドの2番目のパラメータをFalseにして呼び出すと、例外がスローされます。
上記以外の、ThreadExceptionイベントの注意点を以下に列挙します。
AppDomain.UnhandledExceptionイベントによっても、捕捉されていない例外を調べることができます。
UnhandledExceptionイベントはThreadExceptionイベントとは違い、UIスレッド以外のスレッドで例外がスローされた場合でも発生します。また、Windowsフォームアプリケーションだけでなく、コンソールアプリケーションでも使用できます。
さらにThreadExceptionイベントとは違い、UnhandledExceptionイベントハンドラが呼び出された後も例外が無視されるということはなく、そのままにすると通常は「(アプリケーション)は動作を停止しました。」のようなダイアログが表示され、アプリケーションは終了します。
以下にUnhandledExceptionイベントを使用した例を示します。この例はコンソールアプリケーションです。
Module Module1 'エントリポイント Sub Main(args As String()) 'UnhandledExceptionイベントハンドラを追加する AddHandler System.AppDomain.CurrentDomain.UnhandledException, _ AddressOf CurrentDomain_UnhandledException 'または、次のようにもできる 'Dim curDom As System.AppDomain = System.Threading.Thread.GetDomain() 'AddHandler curDom.UnhandledException, _ ' AddressOf CurrentDomain_UnhandledException '例外をスローする Throw New Exception("エラーのテストです。") Console.ReadLine() End Sub 'UnhandledExceptionイベントハンドラ Sub CurrentDomain_UnhandledException(sender As Object, _ e As UnhandledExceptionEventArgs) Try Dim ex As Exception = DirectCast(e.ExceptionObject, Exception) 'エラーメッセージを表示する Console.WriteLine("エラー: {0}", ex.Message) Finally 'アプリケーションを終了する Environment.Exit(1) End Try End Sub End Module
public class Program { //エントリポイント static void Main(string[] args) { //UnhandledExceptionイベントハンドラを追加する System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); //または、次のようにもできる //System.AppDomain curDom = System.Threading.Thread.GetDomain(); //curDom.UnhandledException += // new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); //例外をスローする throw new Exception("エラーのテストです。"); Console.ReadLine(); } //UnhandledExceptionイベントハンドラ static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { try { Exception ex = (Exception)e.ExceptionObject; //エラーメッセージを表示する Console.WriteLine("エラー: {0}", ex.Message); } finally { //アプリケーションを終了する Environment.Exit(1); } } }
補足:UnhandledExceptionイベントハンドラでアプリケーションを終了させる時、Environment.ExitではなくApplication.Exitメソッドを使うと、「(アプリケーション)は動作を停止しました。」というダイアログが表示されてしまいます。
Windowsフォームアプリケーションで捕捉されなかったすべての例外を調べるには、基本的には、ThreadExceptionイベントとUnhandledExceptionイベントの両方を使います。UIスレッドで例外がスローされた時はThreadExceptionイベントが発生し、それ以外ではUnhandledExceptionイベントが発生します。
もしUnhandledExceptionイベントをUIスレッドの例外でも発生させたいのであれば、SetUnhandledExceptionModeメソッドにThrowExceptionを指定して呼び出します。
'Imports System.Windows.Forms ''' <summary> ''' アプリケーションのメイン エントリ ポイントです。 ''' </summary> <STAThread> _ Public Shared Sub Main() 'ThreadExceptionイベントハンドラを追加 AddHandler Application.ThreadException, _ AddressOf Application_ThreadException 'ThreadExceptionが発生しないようにする Application.SetUnhandledExceptionMode( _ UnhandledExceptionMode.ThrowException) 'UnhandledExceptionイベントハンドラを追加 AddHandler System.AppDomain.CurrentDomain.UnhandledException, _ AddressOf CurrentDomain_UnhandledException Application.Run(New Form1()) End Sub 'UnhandledExceptionイベントハンドラ Private Shared Sub CurrentDomain_UnhandledException(sender As Object, _ e As UnhandledExceptionEventArgs) Try 'エラーメッセージを表示する MessageBox.Show(DirectCast(e.ExceptionObject, Exception).Message, _ "エラー") Finally 'アプリケーションを終了する Environment.Exit(1) End Try End Sub 'ThreadExceptionイベントハンドラ Private Shared Sub Application_ThreadException(sender As Object, _ e As System.Threading.ThreadExceptionEventArgs) Try 'エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー") Finally 'アプリケーションを終了する Application.Exit() End Try End Sub
//using System.Windows.Forms; /// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] private static void Main() { //ThreadExceptionイベントハンドラを追加 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler( Application_ThreadException); //ThreadExceptionが発生しないようにする Application.SetUnhandledExceptionMode( UnhandledExceptionMode.ThrowException); //UnhandledExceptionイベントハンドラを追加 System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Application.Run(new Form1()); } //UnhandledExceptionイベントハンドラ private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { try { //エラーメッセージを表示する MessageBox.Show(((Exception)e.ExceptionObject).Message, "エラー"); } finally { //アプリケーションを終了する Environment.Exit(1); } } //ThreadExceptionイベントハンドラ private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { try { //エラーメッセージを表示する MessageBox.Show(e.Exception.Message, "エラー"); } finally { //アプリケーションを終了する Application.Exit(); } }
上記以外のUnhandledExceptionイベントの注意点を以下に列挙します。
Visual Studio 2005以降のVB.NETでは、My.Application.UnhandledExceptionイベントを使用することができます。
Application.UnhandledExceptionイベントを使用するには、アプリケーションフレームワークが有効になっている必要があります。デフォルトで有効になっていますが、無効になっている場合は、プロジェクトのプロパティの「アプリケーション」タブの「アプリケーションフレームワークを有効にする」にチェックを入れます。
Application.UnhandledExceptionイベントは、Application.ThreadExceptionイベントとほぼ同じと考えて良さそうです。ただし、イベントハンドラでUnhandledExceptionEventArgs.ExitApplicationプロパティをTrueにすることでアプリケーションを終了させることができるという機能が加わっています。
My.Application.UnhandledExceptionイベントハンドラは、ApplicationEvents.vbに記述すると便利です。ApplicationEvents.vbは、プロジェクトのプロパティの「アプリケーション」タブにある「アプリケーションイベントの表示」ボタンをクリックすることにより表示できます。(プロジェクトのプロパティは、メニューの「プロジェクト」-「プロパティ」で表示できます。)
以下にApplicationEvents.vbにUnhandledExceptionイベントハンドラを記述した例を示します。
Namespace My Partial Friend Class MyApplication Private Sub MyApplication_UnhandledException( _ ByVal sender As Object, _ ByVal e As Microsoft.VisualBasic.ApplicationServices. _ UnhandledExceptionEventArgs) _ Handles Me.UnhandledException Try 'e.ExitApplicationをTrueにすると、アプリケーションが終了する 'デフォルトでTrueなので、必要ない e.ExitApplication = True MsgBox(e.Exception.Message, MsgBoxStyle.Critical, "エラー") Finally 'アプリケーションを終了する Application.Exit() End Try End Sub End Class End Namespace
これらのイベントを使っても例外を捕捉できないケースがあります。そのようなケースについて、知っている限り紹介します。