Windowsフォーム・アプリケーションやコンソール・アプリケーションを実装する際、例外が発生する可能性がある個所では、基本的に、Try-Catch構文によりその例外をキャッチして適切な処置を施す必要がある。しかし現実には、例外が正しくキャッチ(=トラップ)されていないというケースは多々あり、その場合にはアプリケーションの実行中に次のような.NET Framework標準のエラー・ダイアログが表示されてしまうことになる。
このエラー・ダイアログは、見て分かるとおり、一般的なユーザーにとって分かりやすいものとはいえない。パソコンの操作に自信のない人であれば、これを見た途端に困惑してしまうということもあるだろう。
このような事態を避けるために、.NET標準のエラー・ダイアログを、もっとユーザー・フレンドリな自作のエラー・ダイアログに切り替えたいという要望も、きっと多いのではないだろうか。
そこで本稿では、Windowsアプリケーションやコンソール・アプリケーションで処理されていない例外を、1カ所でまとめてハンドルしてしまう方法を紹介する。この方法によりハンドルしたメソッド内で、独自に作成したダイアログを表示するようにすれば、前述の要望も実現可能である。
処理されていない例外をハンドルするには?
処理されていない例外をハンドルするには、以下の2つの方法が用意されている。
1. Application.ThreadExceptionイベントの活用
処理されなかった例外をハンドルするには、WindowsフォームならApplicationクラス(System.Windows.Forms名前空間)のThreadExceptionイベントをハンドルして処理すればよい。このイベントは、Windowsフォーム・アプリケーションのメイン・スレッド(=ApplicationクラスのRunメソッドにより実行されるアプリケーションのコンテキスト)内で発生した未処理の例外をハンドルするためのものである。
2. Thread.GetDomain().UnhandledExceptionイベントの活用
それでは、上記のメイン・スレッド以外のコンテキスト上で発生した例外は、どのようにしてハンドルすればよいのだろうか? これを行うのが、現在のアプリケーション・ドメイン(=AppDomainクラス(System名前空間)のオブジェクトにより表され、Thread.GetDomainメソッドにより取得できる)のUnhandledExceptionイベントである。
例えば、マルチスレッド・アプリケーションなどにおいて、メイン・スレッド以外のスレッドで発生した例外や、コンソール・アプリケーションで発生した例外などは、すべてこのUnhandledExceptionイベントをハンドルして処理すればよい。
以上、これら2つのイベントを活用して未処理の例外を実際に処理しているのが、次のサンプル・プログラムだ。
using System;
using System.Windows.Forms;
using System.Threading;
namespace WindowsApplication1
{
public class Program
{
[STAThread]
static void Main()
{
// ThreadExceptionイベント・ハンドラを登録する
Application.ThreadException += new
ThreadExceptionEventHandler(Application_ThreadException);
// UnhandledExceptionイベント・ハンドラを登録する
Thread.GetDomain().UnhandledException += new
UnhandledExceptionEventHandler(Application_UnhandledException);
// メイン・スレッド以外の例外はUnhandledExceptionでハンドル
//string buffer = "1"; char error = buffer[2];
// ここで実行されるメイン・アプリケーション・スレッドの例外は
// Application_ThreadExceptionでハンドルできる
Application.Run(new Form1());
}
// 未処理例外をキャッチするイベント・ハンドラ
// (Windowsアプリケーション用)
public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
ShowErrorMessage(e.Exception, "Application_ThreadExceptionによる例外通知です。");
}
// 未処理例外をキャッチするイベント・ハンドラ
// (主にコンソール・アプリケーション用)
public static void Application_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
if (ex != null)
{
ShowErrorMessage(ex, "Application_UnhandledExceptionによる例外通知です。");
}
}
// ユーザー・フレンドリなダイアログを表示するメソッド
public static void ShowErrorMessage(Exception ex, string extraMessage)
{
MessageBox.Show(extraMessage + " \n――――――――\n\n" +
"エラーが発生しました。開発元にお知らせください\n\n" +
"【エラー内容】\n" + ex.Message + "\n\n" +
"【スタックトレース】\n" + ex.StackTrace);
}
}
}
Imports System.Threading
Public Class Program
<STAThread()> _
Shared Sub Main()
' ThreadExceptionイベント・ハンドラを登録する
AddHandler Application.ThreadException, AddressOf Application_ThreadException
' UnhandledExceptionイベント・ハンドラを登録する
AddHandler Thread.GetDomain().UnhandledException, AddressOf Application_UnhandledException
' メイン・スレッド以外の例外はUnhandledExceptionでハンドル
'Dim buffer As String = "1"
'Dim [error] As Char = buffer.Chars(2)
' ここで実行されるメイン・アプリケーション・スレッドの例外は
' Application_ThreadExceptionでハンドルできる
Application.Run(New Form1)
End Sub
' 未処理例外をキャッチするイベント・ハンドラ
' (Windowsアプリケーション用)
Public Shared Sub Application_ThreadException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
ShowErrorMessage(e.Exception, "Application.ThreadExceptionによる例外通知です。")
End Sub
' 未処理例外をキャッチするイベント・ハンドラ
' (主にコンソール・アプリケーション用)
Public Shared Sub Application_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
Dim ex As Exception = CType(e.ExceptionObject, Exception)
If Not ex Is Nothing Then
ShowErrorMessage(ex, "Application.UnhandledExceptionによる例外通知です。")
End If
End Sub
' ユーザー・フレンドリなダイアログを表示するメソッド
Public Shared Sub ShowErrorMessage(ByVal ex As Exception, ByVal extraMessage As String)
MessageBox.Show(extraMessage & vbLf & "――――――――" & _
vbLf & vbLf & _
"エラーが発生しました。開発元にお知らせください" & _
vbLf & vbLf & _
"【エラー内容】" & vbLf & ex.Message & vbLf & vbLf & _
"【スタックトレース】" & vbLf & ex.StackTrace)
End Sub
End Class
このプログラムを実行するには、アプリケーションのエントリポイントを、「ProgramクラスのMainメソッド」に切り替える必要がある。具体的には、Visual Studio .NETの[ソリューション エクスプローラ]でプロジェクト項目を右クリックしてコンテキスト・メニューを表示し、そのメニューから[プロパティ]を選択して[<プロジェクトの> プロパティ ページ]ダイアログを開き、そのダイアログの[共通プロパティ]−[全般]の[スタートアップの設定]の値を「Sub Main」に設定し直せばよい。
サンプル・プログラム(program.vb)のダウンロード
Windowsフォーム(form1.vb)のダウンロード
このサンプル・プログラムを実行したのが次の画面である。
ここで作成しているダイアログはあまり良い例とはいえないが、より使いやすく、見やすく作り込むことで、ユーザビリティの高いエラー通知ダイアログにすることができるだろう。
なおVisual Studio .NETがインストールされている環境などでは、その設定内容によっては、上記に示した例外をハンドルするためのイベントが発生する前に、デバッガを選択するための[Just-In-Time デバッグ]ダイアログが表示されることがある。このダイアログが表示されるのを抑止する方法については、「TIPS:[アプリケーション・エラー]ダイアログを非表示にするには?」を参照してほしい。
カテゴリ:Windowsフォーム 処理対象:例外
カテゴリ:コンソール・アプリケーション 処理対象:例外
使用ライブラリ:Applicationクラス(System.Windows.Forms名前空間)
使用ライブラリ:AppDomainクラス(System名前空間)
関連TIPS:[アプリケーション・エラー]ダイアログを非表示にするには?
Copyright© Digital Advantage Corp. All Rights Reserved.