WPF 응용 프로그램에서 전역 적으로 예외를 포착합니까?


242

우리는 런타임에 일부가 예외를 throw 할 수있는 WPF 응용 프로그램을 가지고 있습니다. 처리되지 않은 예외를 전역 적으로 잡아서 기록하고 싶지만 아무 일도없는 것처럼 프로그램 실행을 계속하고 싶습니다 (VB와 같은 On Error Resume Next).

C #에서 가능합니까? 그렇다면 예외 처리 코드를 정확히 어디에 두어야합니까?

현재 나는 try/ catcharound를 감싸고 발생할 수있는 모든 예외를 포착 할 수있는 단일 지점을 볼 수 없습니다 . 그리고 그때에도 나는 잡기로 인해 처형 된 것을 남겼습니다. 아니면 내가 끔찍하게 잘못된 방향으로 생각하고 있습니까?

ETA : 아래의 많은 사람들이 지적한 바에 따르면 :이 응용 프로그램은 원자력 발전소를 제어하기위한 것이 아닙니다. 충돌이 발생하면 그다지 중요하지 않지만 대부분 UI와 관련된 임의의 예외는 사용되는 컨텍스트에서 성가신 것입니다. 그중 몇 가지가 있었으며 플러그인 아키텍처를 사용하고 다른 사람들에 의해 확장 될 수 있기 때문에 (이 경우 학생도 가능하므로 완전히 오류가없는 코드를 작성할 수있는 숙련 된 개발자는 없습니다 ).

잡힌 예외에 관해서는 완전한 스택 추적을 포함하여 로그 파일에 기록합니다. 그것이 그 운동의 요점이었습니다. VB의 OERN에 ​​비유 한 사람들을 문자 그대로 대하는 것입니다.

특정 클래스의 오류를 맹목적으로 무시하는 것은 위험하며 애플리케이션 인스턴스가 손상 될 수 있음을 알고 있습니다. 앞에서 말했듯이,이 프로그램은 누구에게도 중요하지 않습니다. 그들의 올바른 마음에 아무도 인류 문명의 생존에 내기를 걸었을 것입니다. 그것은 특정 디자인 접근법을 테스트하기위한 작은 도구 일뿐입니다. 소프트웨어 공학.

응용 프로그램을 즉시 사용하기 위해 예외에서 발생할 수있는 일이 많지 않습니다.

  • 예외 처리 없음 – 오류 대화 상자 및 응용 프로그램 종료. 다른 주제와 마찬가지로 실험을 반복해야합니다. 불행히도 오류가 기록되지 않았습니다.
  • 일반적인 예외 처리 – 양성 오류 포착, 피해 없음. 이것은 개발 과정에서 우리가보고 있던 모든 오류로 판단되는 일반적인 사례입니다. 이런 종류의 오류를 무시해도 즉각적인 결과는 없습니다. 핵심 데이터 구조는이를 쉽게 극복 할 수있을 정도로 충분히 테스트되었습니다.
  • 일반적인 예외 처리 – 심각한 오류가 발생하여 나중에 중단 될 수 있습니다. 거의 발생하지 않을 수 있습니다. 우리는 지금까지 본 적이 없습니다. 어쨌든 오류가 기록되고 충돌이 불가피 할 수 있습니다. 이것은 개념적으로 첫 번째 경우와 유사합니다. 스택 추적을 제외하고. 그리고 대부분의 경우 사용자는 눈치 채지 못할 것입니다.

프로그램에 의해 생성 된 실험 데이터의 경우 : 심각한 오류는 최악의 경우 데이터를 기록하지 않습니다. 실험 결과를 약간 변화시키는 미묘한 변화는 거의 없을 것입니다. 이 경우에도 결과가 모호한 것처럼 보이면 오류가 기록되었습니다. 전체 이상치 인 경우 해당 데이터 포인트를 버릴 수 있습니다.

요약하자면, 그렇습니다. 나는 여전히 부분적으로 제정신이라고 생각하며 프로그램을 실행하는 것이 완전히 악의적 인 전역 예외 처리 루틴을 고려하지 않습니다. 전에 두 번 언급했듯이, 그러한 결정은 응용 프로그램에 따라 유효 할 수 있습니다. 이 경우에 그것은 유효한 결정으로 판결되었고 총체적이고 완전한 헛소리가 아닙니다. 다른 응용 프로그램의 경우 결정이 다르게 보일 수 있습니다. 그러나 저 또는 그 프로젝트에서 일한 다른 사람들이 우리가 실수를 무시하고 있기 때문에 세상을 날려 버릴 수 있다고 비난하지 마십시오.

참고 : 해당 응용 프로그램에는 정확히 한 명의 사용자가 있습니다. Windows 또는 Office와 같이 수백만 명의 사람들이 사용하는 예외는 아닙니다. 사용자에게 예외가 발생하는 비용은 처음부터 매우 다릅니다.


이것은 실제로 생각하지 않습니다. 예, 아마도 응용 프로그램을 종료하고 싶을 것입니다. 그러나 먼저 StackTrace로 예외를 기록하는 것이 좋지 않습니까? 사용자에게 "모두이 버튼을 눌렀을 때 응용 프로그램이 중단되었습니다"라는 정보 만 있으면 충분한 정보가 없기 때문에 문제를 해결할 수 없을 수도 있습니다. 그러나 응용 프로그램을보다 즐겁게 중단하기 전에 먼저 예외를 기록한 경우 훨씬 더 많은 정보를 얻게됩니다.
Russ

나는 그 질문에 대해 그 점을 좀 더 자세히 설명했다. 나는 관련된 위험과 그 특정 응용 프로그램에 대해 허용되는 것으로 알고 있습니다. UI가 멋진 애니메이션을 만들려고 시도하는 동안 경계에서 벗어난 인덱스만큼 간단한 것으로 응용 프로그램을 중단하는 것은 과도하고 불필요한 것입니다. 예, 정확한 원인을 모르지만 대부분의 오류 사례가 양성이라는 주장을 뒷받침 할 데이터가 있습니다. 우리가 마스킹하는 심각한 문제는 응용 프로그램을 중단시킬 수 있지만 전역 예외 처리없이 발생했을 수 있습니다.
Joey

또 다른 참고 사항 :이 접근법으로 충돌을 방지하면 사용자가 좋아할 것입니다.
lahjaton_j

Visual Studio 2010 용 C #의 WPF (가장 완전한 처리기 모음) 샘플에서 처리되지 않은 예외 처리를 참조하십시오 . AppDomain.CurrentDomain.FirstChanceException, Application.DispatcherUnhandledException 및 AppDomain.CurrentDomain.UnhandledException을 포함하여 5 개의 예제가 있습니다.
user34660

On Error Resume NextC # 에서는 VB와 같은 코드 흐름 이 불가능하다는 것을 추가하고 싶습니다 . 애프터 Exception실행이 계속됩니다 (C 번호는 "오류"가없는) 당신은 단순히 다음 문을 다시 시작할 수 없습니다 catch블록 - 이하 답변에 설명 된 이벤트 핸들러의 일인치
마이크

답변:


191

를 사용하십시오 Application.DispatcherUnhandledException Event. 요약을 보려면 이 질문 을 참조하십시오 ( Drew Noakes의 답변 참조 ).

데이터베이스에 저장하려고 할 때 스택 오버플로, 메모리 부족 또는 네트워크 연결 끊김 후와 같이 응용 프로그램을 성공적으로 다시 시작하지 못하게하는 예외가 여전히 있습니다.


감사. 그리고 네, 나는 회복 할 수없는 예외가 있음을 알고 있지만이 경우 발생할 수있는 대부분은 양성입니다.
Joey

14
백그라운드 스레드 (예 : ThreadPool.QueueUserWorkItem 사용)에서 예외가 발생하면 작동하지 않습니다.
Szymon Rozga 2009

@ siz : 좋은 지적이지만, 작업 항목에서 일반적인 try 블록으로 처리 할 수 ​​있습니까?
David Schmitt

1
@PitiOngmongkolkul : 핸들러는 메인 루프에서 이벤트로 호출됩니다. 이벤트 핸들러가 반환되면 앱은 정상적으로 계속됩니다.
David Schmitt

4
프로그램을 종료하는 기본 핸들러를 건너 뛰려면 e.Handled = true를 설정해야합니다. 여기서 e는 DispatcherUnhandledExceptionEventArgs입니다. msdn.microsoft.com/ko-kr/library/…
Piti Ongmongkolkul

55

AppDomain의 모든 스레드 , UI 디스패처 스레드비동기 함수 에서 발생한 예외를 포착 하는 NLog 사용 예제 코드 :

App.xaml.cs :

public partial class App : Application
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        SetupExceptionHandling();
    }

    private void SetupExceptionHandling()
    {
        AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");

        DispatcherUnhandledException += (s, e) =>
        {
            LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");
            e.Handled = true;
        };

        TaskScheduler.UnobservedTaskException += (s, e) =>
        {
            LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
            e.SetObserved();
        };
    }

    private void LogUnhandledException(Exception exception, string source)
    {
        string message = $"Unhandled exception ({source})";
        try
        {
            System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
            message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Exception in LogUnhandledException");
        }
        finally
        {
            _logger.Error(exception, message);
        }
    }

10
이것은 가장 완벽한 대답입니다! 택스 스케줄러 예외가 포함되어 있습니다. 깨끗하고 간단한 코드입니다.
Nikola Jovic

3
최근 고객에게 App.config가 손상되어 NLog가 App.config에서 읽으려고 시도했지만 예외가 발생하여 앱이 시작되지 않았습니다. 그 예외는 정적 로거 초기화 프로그램에 있었기 때문에 UnhandledException핸들러에 의해 포착되지 않았습니다 . 무슨 일이 있었는지 알아 보려면 Windows Event Log Viewer를 살펴 봐야했습니다.
heltonbiker

나는 세트에 권하고 e.Handled = true;상기 UnhandledException응용 프로그램이 UI 예외에 충돌하지 않습니다
Apfelkuacha

30

AppDomain.UnhandledException 이벤트

이 이벤트는 포착되지 않은 예외에 대한 알림을 제공합니다. 시스템 기본 핸들러가 예외를 사용자에게보고하고 애플리케이션을 종료하기 전에 애플리케이션이 예외에 대한 정보를 로그 할 수 있습니다.

   public App()
   {
      AppDomain currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);    
   }

   static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }

UnhandledException 이벤트가 기본 응용 프로그램 도메인에서 처리되는 경우 스레드가 시작된 응용 프로그램 도메인에 관계없이 스레드에서 처리되지 않은 예외가 발생하면 이벤트가 발생합니다. UnhandledException에 대한 이벤트 처리기가있는 응용 프로그램 도메인에서 스레드가 시작된 경우, 해당 응용 프로그램 도메인에서 이벤트가 발생합니다. 해당 응용 프로그램 도메인이 기본 응용 프로그램 도메인이 아니고 기본 응용 프로그램 도메인에도 이벤트 핸들러가있는 경우 두 응용 프로그램 도메인 모두에서 이벤트가 발생합니다.

예를 들어 스레드가 응용 프로그램 도메인 "AD1"에서 시작하고 응용 프로그램 도메인 "AD2"의 메서드를 호출 한 다음 응용 프로그램 도메인 "AD3"의 메서드를 호출한다고 가정하면 예외가 발생합니다. UnhandledException 이벤트가 발생할 수있는 첫 번째 응용 프로그램 도메인은 "AD1"입니다. 해당 응용 프로그램 도메인이 기본 응용 프로그램 도메인이 아닌 경우 기본 응용 프로그램 도메인에서 이벤트가 발생할 수도 있습니다.


콘솔 응용 프로그램 및 WinForms 응용 프로그램에 대해서만 언급 한 URL에서 복사 : ".NET Framework 4부터는이 프로세스는 스택 오버플로 또는 이벤트 핸들러가 보안에 중요하지 않고 HandleProcessCorruptedStateExceptionsAttribute 속성이없는 경우 액세스 위반. "
George Birbilis

1
@GeorgeBirbilis : 앱 생성자에서 UnhandledException 이벤트를 구독 할 수 있습니다.
CharithJ

18

또한 다른 사람들이 여기에 언급 한 내용 Application.DispatcherUnhandledException과 (및 유사 항목 )을

<configuration>
  <runtime>  
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

에서 app.config보조 스레드 예외가 응용 프로그램을 종료하지 못하게합니다.


2

다음은 다음을 사용한 완전한 예입니다. NLog

using NLog;
using System;
using System.Windows;

namespace MyApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public App()
        {
            var currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception)e.ExceptionObject;
            logger.Error("UnhandledException caught : " + ex.Message);
            logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
            logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
        }        
    }


}

-4

"VB의 On Error Resume Next?"와 같이 좀 무섭다. 첫 번째 권장 사항은 그렇게하지 않는 것입니다. 두 번째 권장 사항은하지 말고 생각하지 마십시오. 당신은 당신의 잘못을 더 잘 분리해야합니다. 이 문제에 접근하는 방법은 코드가 어떻게 구성되어 있는지에 달려 있습니다. MVC와 같은 패턴을 사용하는 경우 이것은 어렵지 않으며 전역 예외 삼키기가 필요하지 않습니다. 둘째, log4net과 같은 좋은 로깅 라이브러리를 찾거나 추적을 사용하십시오. 어떤 종류의 예외에 대해 이야기하고 있고 어떤 부분에서 예외가 발생할 수 있는지에 대한 자세한 내용을 알아야합니다.


2
요점은 잡히지 않은 예외 후에도 응용 프로그램을 계속 실행하고 싶다는 것입니다. 나는 단지 그것들을 기록하고 싶다 (필요에 따라 맞춤형 로깅 프레임 워크가있다). 일부 플러그인이 사용자 상호 작용 코드에서 이상한 일을했기 때문에 전체 프로그램을 중단 할 필요가 없다. 내가 지금까지 본 것에서 서로 다른 부분이 실제로 서로를 알지 못하기 때문에 원인을 깨끗하게 분리하는 것은 쉬운 일이 아닙니다.
Joey

9
이해합니다. 그러나 검사하지 않은 예외 이후에는 계속주의해야합니다. 데이터가 손상 될 가능성 등이 있습니다.
BobbyShaftoe

3
BobbyShaftoe에 동의합니다. 이것은 올바른 방법이 아닙니다. 응용 프로그램이 중단되도록하십시오. 결함이 일부 플러그인에있는 경우 플러그인을 수정하거나 누군가가 수정하도록하십시오. 실행 상태로두면 매우 위험합니다. 이상한 부작용이 발생하여 애플리케이션에서 발생하는 버그에 대한 논리적 설명을 찾을 수없는 지점에 도달하게됩니다.

1
나는 그 질문에 대해 그 점을 좀 더 자세히 설명했다. 나는 관련된 위험과 그 특정 응용 프로그램에 대해 허용되는 것으로 알고 있습니다. UI가 멋진 애니메이션을 시도하는 동안 경계에서 벗어난 인덱스만큼 간단한 것으로 응용 프로그램을 중단하는 것은 과도하고 불필요한 것입니다. 예, 정확한 원인을 모르지만 대부분의 오류 사례가 양성이라는 주장을 뒷받침 할 데이터가 있습니다. 우리가 마스킹하는 심각한 문제는 응용 프로그램을 중단시킬 수 있지만 전역 예외 처리없이 발생했을 수 있습니다.
Joey

이것은 답변이 아닌 의견이어야합니다. "당신은 아마해서는 안됩니다"와 함께 "어떻게해야합니까"에 응답하는 것은 대답이 아닙니다, 그것은 의견입니다.
조쉬 노
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.