web-dev-qa-db-ja.com

コンソールアプリケーションの.NETグローバル例外ハンドラー

質問:コンソールアプリケーションで未処理の例外のグローバル例外ハンドラーを定義したい。 asp.netでは、global.asaxで定義できます。Windowsアプリケーション/サービスでは、次のように定義できます。

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

しかし、コンソールアプリケーションのグローバル例外ハンドラをどのように定義できますか?
currentDomainは機能していないようです(.NET 2.0)?

編集:

ああ、愚かな間違い。
VB.NETでは、currentDomainの前に「AddHandler」キーワードを追加する必要があります。そうしないと、IntelliSenseでUnhandledExceptionイベントが表示されません...
これは、VB.NETコンパイラとC#コンパイラがイベント処理を異なる方法で処理するためです。

183
Stefan Steiger

いいえ、それが正しい方法です。これは正確に機能するはずで、おそらく次のように機能します。

using System;

class Program {
    static void Main(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

この方法では、ジッターによって生成されたタイプおよびファイルのロード例外をキャッチできないことに注意してください。 Main()メソッドが実行を開始する前に発生します。これらをキャッチするには、ジッターを遅らせ、危険なコードを別のメソッドに移動し、[MethodImpl(MethodImplOptions.NoInlining)]属性を適用する必要があります。

263
Hans Passant

シングルスレッドのアプリケーションを使用している場合は、Main関数で単純なtry/catchを使用できますが、Main関数の外部、たとえば他のスレッドでスローされる例外は対象外です(他のコメント)。このコードは、Mainで処理しようとしても例外がどのようにアプリケーションを終了させるかを示しています(Enterキーを押してプログラムを正常に終了し、例外が発生する前にアプリケーションを正常に終了できるようにしますが、 、それは非常に不幸に終了します):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

アプリケーションが終了する前に別のスレッドが例外をスローしていくつかのクリーンアップを実行するときの通知を受け取ることができますが、私が知る限り、例外を処理しない場合、コンソールアプリケーションからアプリケーションの実行を強制することはできませんいくつかのあいまいな互換性オプションを使用せずにスローされたスレッドで、アプリケーションが.NET 1.xの場合と同じように動作するようにします。このコードは、他のスレッドからの例外をメインスレッドに通知する方法を示していますが、それでも不幸に終了します。

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

だから、私の意見では、それを処理する最もクリーンな方法はコンソールアプリケーションですべてのスレッドがルートレベルで例外ハンドラを持つようにすることです:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}
23
BlueMonkMN

スレッドからの例外も処理する必要があります。

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

申し訳ありませんが、winformの場合、コンソールアプリケーションで使用しているスレッドについては、try/catchブロックで囲む必要があります。未処理の例外に遭遇したバックグラウンドスレッドは、アプリケーションを終了させません。

12
BlackICE