Windows Forms (.NET 1.1) での例外処理

GDNJ の Application.RunとExceptionのCatch より、知らなくても困らない補足。
Windows Forms アプリケーションのウィンドウズメッセージディスパッチャは、メッセージディスパッチ中にハンドルされない例外を受け取ると、次の3点を確認する。

  • Application.ThreadException は設定されているか?
  • デバッガはアタッチされているか?
  • デバッグ可能なアセンブリデバッグすることを許可された環境で動かしているか?

この3点のうちどれかが真である場合、メッセージディスパッチャは例外に対して何も処理せず上位の例外ハンドラへと制御を返す。*1 そして、すべてが偽である場合は System.Windows.Forms.ThreadExceptionDialog が表示される。

Application.ThreadException

まず、Application.ThreadException だが、このイベントは現在スレッドの ThreadContext.ThreadException へ簡単にアクセスするための手段でしかないため、異なるスレッドから参照すると異なる結果になるということに注意しましょう。そして、Windows Forms のフレームワークでは、前述のすべての条件の確認をすべてのスレッドで通して1度だけしか行わないという点に非常に注意しなければならない。
System.Windows.Forms の内部処理が1度でもこのチェックを済ましてしまうと、以降 ThreadException イベントにハンドラを設定しても手遅れであること、複数の UI スレッドを構築する場合、最初にソコにアクセスしたスレッドが Application.ThreadException を設定していなければ、別のスレッドで正しく Application.ThreadException を設定していても無駄である、ということ。これは下手をするとハマります。

デバッガはアタッチされているか?

System.Diagnostics.Debugger.IsAttached と同様に、Managed Debugger がアタッチされているかどうかが判断基準となる。Win32 Native Debugger をアタッチしてもデバッガはアタッチされていないと判断される。

デバッグ可能なアセンブリデバッグすることを許可された環境で動かしているか?

デバッグ可能な環境であるかどうかは、.NET Framework では2箇所の設定場所があります。1つはレジストリの HKLM\Software\Microsoft\.NETFramework であり、.NET Framework 開発者ガイドによると、

  • 値が 0 の場合は、メッセージ ボックスを表示してユーザーの確認を求めます。
  • 値が 1 の場合は、制御が戻されます。この結果、スタック ダンプが作成され、プロセスが終了します。
  • 値が 2 の場合は、DbgManagedDebugger レジストリ キーに登録されているデバッガを呼び出します。

となっていますが、この場合に重要なことはデバッグが可能かどうかですので、設定値が 1 でなければデバッグ可能な環境であると判断されることになります。また、アプリケーション構成ファイルにて、System.Windows.Forms セクションに jitDebugging というキーがあり、この値が true であることも必要な条件に含まれます。*2
デバッグ可能なアセンブリであるかどうかという判断は、アセンブリの DebuggableAttribute 属性によって判断されるようで、C# あればコンパイル時に /debug+ によってデバッグ可能であるとマーキングすることができます。

*1:通常は、メッセージループの呼び出し元である Application.Run() や Form.ShowDialog() へ戻ることになる。

*2:マシン構成ファイルにて設定できますが、コメントとして記載されているように、この設定は通常個別のアプリケーション用のアプリケーション構成ファイルで変更します。