콘솔 응용 프로그램의 .NET 전역 예외 처리기


198

질문 : 콘솔 응용 프로그램에서 처리되지 않은 예외에 대한 전역 예외 처리기를 정의하고 싶습니다. 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 # 컴파일러가 이벤트 처리를 다르게 처리하기 때문입니다.

답변:


283

아니요, 올바른 방법입니다. 이것은 당신이 아마 일할 수있는 것만 큼 정확하게 작동했습니다 :

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)] 속성을 적용하십시오.


3
여기서 제안한 것을 구현했지만 응용 프로그램을 종료하고 싶지 않습니다. 그냥 기록하고 프로세스를 계속 진행하고 싶습니다 ( Console.ReadLine()프로그램 흐름에 방해가 되지 않습니다). 그러나 내가 얻는 것은 예외가 계속해서 또 다시 발생하는 것입니다.

3
@Shahrooz Jefri : 처리되지 않은 예외가 발생하면 계속할 수 없습니다. 스택이 엉망이되어 터미널입니다. 서버가있는 경우 UnhandledExceptionTrapper에서 수행 할 수있는 작업은 동일한 명령 줄 인수를 사용하여 프로그램을 다시 시작하는 것입니다.
Stefan Steiger

6
가장 확실합니다! 여기서는 Application.ThreadException 이벤트에 대해 이야기하지 않습니다.
Hans Passant

4
이 답변과 답글의 주석을 이해하는 열쇠는이 코드가 예외를 감지하는 것을 보여 주지만 계속 시도 할 수있는 일반적인 try / catch 블록에서와 같이 예외를 완전히 "처리"하지 못한다는 것을 인식하는 것입니다. . 자세한 내용은 다른 답변을 참조하십시오. 이런 방식으로 예외를 "처리"하면 다른 스레드에서 예외가 발생할 때 계속 실행할 수있는 옵션이 없습니다. 그런 의미에서 "처리"란 "프로세스 및 계속 실행"을 의미하는 경우이 답변이 예외를 완전히 "처리"하지 않았다고 말할 수 있습니다.
BlueMonkMN

4
다른 스레드에서 실행할 많은 기술이 있습니다. 예를 들어 태스크 병렬 라이브러리 (TPL)는 처리되지 않은 예외를 포착하는 고유 한 방법이 있습니다. 따라서 이것이 모든 상황에서 작동하지 않는다고 말하는 것은 어리석은 일입니다 .C #에는 모든 것을 다룰 수있는 곳이 없습니다. 그러나 사용하는 기술에 따라 연결할 수있는 다양한 장소가 있습니다.
Doug

23

단일 스레드 응용 프로그램이있는 경우 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");
   }
}

TRY CATCH는 예기치 않은 오류로 인해 릴리스 모드에서 작동하지 않습니다 : /
Muflix

12

또한 스레드의 예외를 처리해야합니다.

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

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

Whoop, 죄송합니다. winforms, 콘솔 응용 프로그램에서 사용하는 모든 스레드에 대해 try / catch 블록으로 묶어야합니다. 처리되지 않은 예외가 발생하는 백그라운드 스레드는 응용 프로그램을 종료시키지 않습니다.


1

방금 이전 VB.NET 콘솔 응용 프로그램을 상속했으며 전역 예외 처리기를 설정해야했습니다. 이 질문은 VB.NET을 몇 번 언급하고 VB.NET으로 태그가 지정되어 있지만 여기에있는 다른 모든 대답은 C #에 있으므로 VB.NET 응용 프로그램의 정확한 구문을 추가 할 것이라고 생각했습니다.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

REMStack Overflow가 구문 강조를 조금 더 잘 처리하는 것처럼 보이므로 작은 따옴표 대신 주석 표시를 사용했습니다 REM.


-13

.Net 2.0 용 MSDN 문서에 따라 노력하고 있습니다. 콘솔 앱의 진입 점을 중심으로 try / catch를 시도해 볼 수도 있습니다.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

그리고 이제 캐치가 잡히지 않은 것을 처리 합니다 (메인 스레드에서 ). 우아하고 원할 때 다시 시작하거나 앱을 죽이고 예외를 기록 할 수 있습니다. 정리하고 싶다면 마지막으로 추가하십시오. 각 스레드는 메인과 유사한 고유 한 예외 처리가 필요합니다.

BlueMonkMN에 의해 ​​지적되고 그의 답변에 자세히 표시된 스레드에 대한 요점을 명확히하기 위해 편집되었습니다.


1
불행히도 실제로 Main () 블록 외부에서는 예외가 발생할 수 있습니다. 이것은 실제로 당신이 생각하는 "모두 잡기"가 아닙니다. @Hans '답변을 참조하십시오.
Mike Atlas

@Mike 먼저 나는 그가하고있는 방식이 정확하며 메인에서 시도 / 캐치를 시도 할 수 있다고 말했습니다. 내가 Hans에 동의했을 때 왜 당신 (또는 다른 사람)이 나에게 투표권을 주 었는지 확실하지 않습니다. 그것은 실제로 공평하지 않으며, Main의 try / catch가 catch 할 수없는 AppDomain UnhandledException 프로세스에 의해 포착 될 수있는 예외에 대한 증거를 제공하지 않고 대안이 잘못되었다고 말할 수 있습니다. 나는 왜 그것이 틀렸다는 것을 증명하지 않고 어떤 것이 잘못되었다고 말하는 것은 무례하다고 생각합니다.
Rodney S. Foley

5
당신이 요구하는 예를 게시했습니다. 그렇지 않은 경우 책임을지고 Mike의 이전 답변에서 관련없는 투표를 제거하십시오. (개인적인 관심은 없으며 시스템의 남용을 보는 것을 좋아하지 않습니다.)
BlueMonkMN

3
그러나 당신은 여전히 ​​똑같은 "게임"을하는데, 그 이유는 답변의 질에 기초한 것이 아니라 순수한 보복이기 때문입니다. 그것은 문제를 해결하는 방법이 아니며, 더 악화시킬뿐입니다. 당신의 답변에 대해 정당한 우려를 가지고있는 사람에 대해 보복 할 때 특히 나쁘다 (증명 한 바와 같이).
BlueMonkMN 2016 년

3
아, 나는 또한 투표율이 "완전한 바보이거나 규칙을 어 기고있는"사람들을위한 것이 아니라 답변의 질을 판단하기위한 것이라고 덧붙였다. 본인에게 제공 한 사람에 대해 "댓글"을 작성하기위한 투표 거부 답변은 해당 투표의 정확성 여부에 상관없이 답변 자체의 내용에 근거한 투표 거부 답변보다 훨씬 더 큰 학대입니다. 개인적으로 취하지 마십시오.
BlueMonkMN
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.