Programing

WPF 전역 예외 처리기

lottogame 2020. 3. 8. 09:52
반응형

WPF 전역 예외 처리기


이 질문에는 이미 답변이 있습니다.

때로는 재현 할 수없는 상황에서 WPF 응용 프로그램이 메시지없이 충돌합니다. 응용 프로그램은 즉시 즉시 닫힙니다.

글로벌 Try / Catch 블록을 구현하기 가장 좋은 곳은 어디입니까? 최소한 "죄송합니다 ..."라는 메시지 상자를 구현해야합니다.


AppDomain.UnhandledException이벤트를 처리 할 수 ​​있습니다

편집 : 실제로이 이벤트는 아마도 더 적합 할 것입니다. Application.DispatcherUnhandledException


처리되지 않은 예외를 다른 수준에서 트랩 할 수 있습니다.

  1. AppDomain.CurrentDomain.UnhandledException AppDomain의 모든 스레드에서.
  2. Dispatcher.UnhandledException 단일 특정 UI 디스패처 스레드에서.
  3. Application.Current.DispatcherUnhandledExceptionWPF 애플리케이션 기본 UI 디스패처 스레드에서
  4. TaskScheduler.UnobservedTaskException 비동기 작업에 작업 스케줄러를 사용하는 각 AppDomain 내에서

처리되지 않은 예외를 트랩하는 데 필요한 레벨을 고려해야합니다.

# 2와 # 3 사이의 결정은 둘 이상의 WPF 스레드를 사용하는지 여부에 따라 다릅니다. 이것은 매우 이국적인 상황이며 자신이 있는지 여부를 잘 모를 경우에는 자신이 아닌 것 같습니다.


Application.Dispatcher.UnhandledException에 대한 간단한 코드 예제 :

public App() {
    this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}

void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {
    string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
    MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    // OR whatever you want like logging etc. MessageBox it's just example
    // for quick debugging etc.
    e.Handled = true;
}

이 코드를 App.xaml.cs에 추가했습니다.


처리되지 않은 예외가 발생할 때마다 "죄송합니다"대화 상자를 표시하기 위해 WPF 앱에서 다음 코드를 사용합니다. 예외 메시지를 표시하고 사용자에게 앱을 닫을 것인지 또는 예외를 무시하고 계속할 것인지 묻습니다 (후자의 경우는 치명적이지 않은 예외가 발생하여 사용자가 계속 앱을 계속 사용할 수있는 경우에 편리함).

App.xaml에서 Startup 이벤트 핸들러를 추가하십시오.

<Application .... Startup="Application_Startup">

App.xaml.cs 코드에서 전역 응용 프로그램 이벤트 처리기를 등록 할 Startup 이벤트 처리기 함수를 추가합니다.

using System.Windows.Threading;

private void Application_Startup(object sender, StartupEventArgs e)
{
    // Global exception handling  
    Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException);    
}

void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{    
    \#if DEBUG   // In debug mode do not custom-handle the exception, let Visual Studio handle it

    e.Handled = false;

    \#else

    ShowUnhandledException(e);    

    \#endif     
}

void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e)
{
    e.Handled = true;

    string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction in the application, and you better close it.\n\nError: {0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)",

    e.Exception.Message + (e.Exception.InnerException != null ? "\n" + 
    e.Exception.InnerException.Message : null));

    if (MessageBox.Show(errorMessage, "Application Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)   {
        if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
    {
        Application.Current.Shutdown();
    } 
}

가장 좋은 대답은 아마 https://stackoverflow.com/a/1472562/601990 입니다.

사용 방법을 보여주는 코드는 다음과 같습니다.

App.xaml.cs

public sealed partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // setting up the Dependency Injection container
        var resolver = ResolverFactory.Get();

        // getting the ILogger or ILog interface
        var logger = resolver.Resolve<ILogger>();
        RegisterGlobalExceptionHandling(logger);

        // Bootstrapping Dependency Injection 
        // injects ViewModel into MainWindow.xaml
        // remember to remove the StartupUri attribute in App.xaml
        var mainWindow = resolver.Resolve<Pages.MainWindow>();
        mainWindow.Show();
    }

    private void RegisterGlobalExceptionHandling(ILogger log)
    {
        // this is the line you really want 
        AppDomain.CurrentDomain.UnhandledException += 
            (sender, args) => CurrentDomainOnUnhandledException(args, log);

        // optional: hooking up some more handlers
        // remember that you need to hook up additional handlers when 
        // logging from other dispatchers, shedulers, or applications

        Application.Dispatcher.UnhandledException += 
            (sender, args) => DispatcherOnUnhandledException(args, log);

        Application.Current.DispatcherUnhandledException +=
            (sender, args) => CurrentOnDispatcherUnhandledException(args, log);

        TaskScheduler.UnobservedTaskException += 
            (sender, args) => TaskSchedulerOnUnobservedTaskException(args, log);
    }

    private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        args.SetObserved();
    }

    private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        // args.Handled = true;
    }

    private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log)
    {
        log.Error(args.Exception, args.Exception.Message);
        // args.Handled = true;
    }

    private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log)
    {
        var exception = args.ExceptionObject as Exception;
        var terminatingMessage = args.IsTerminating ? " The application is terminating." : string.Empty;
        var exceptionMessage = exception?.Message ?? "An unmanaged exception occured.";
        var message = string.Concat(exceptionMessage, terminatingMessage);
        log.Error(exception, message);
    }
}

위의 게시물 외에도 :

Application.Current.DispatcherUnhandledException

메인 스레드 다음에 다른 스레드에서 발생한 예외를 포착하지 않습니다. 실제 스레드에서 이러한 예외를 처리해야합니다. 그러나 전역 예외 처리기에서 처리하려면 주 스레드로 전달할 수 있습니다.

 System.Threading.Thread t = new System.Threading.Thread(() =>
    {
        try
        {
            ...
            //this exception will not be catched by 
            //Application.DispatcherUnhandledException
            throw new Exception("huh..");
            ...
        }
        catch (Exception ex)
        {
            //But we can handle it in the throwing thread
            //and pass it to the main thread wehre Application.
            //DispatcherUnhandledException can handle it
            System.Windows.Application.Current.Dispatcher.Invoke(
                System.Windows.Threading.DispatcherPriority.Normal,
                new Action<Exception>((exc) =>
                    {
                      throw new Exception("Exception from another Thread", exc);
                    }), ex);
        }
    });

토마스의 답변을 보충하기 위해 Application수업에는 DispatcherUnhandledException처리 할 수 있는 이벤트 있습니다.


완벽한 솔루션이 여기 있습니다

샘플 코드로 매우 잘 설명되어 있습니다. 그러나 응용 프로그램을 닫지 않도록주의하십시오. Application.Current.Shutdown (); 앱을 정상적으로 닫습니다.


앞에서 말했다시피

Application.Current.DispatcherUnhandledException은 다른 스레드에서 메인 스레드로 발생한 예외를 포착하지 않습니다.

실은 스레드가 어떻게 만들어 졌는지에 달려 있습니다.

Application.Current.DispatcherUnhandledException이 처리하지 않는 한 가지 사례는 System.Windows.Forms.Timer입니다. 주 스레드가 아닌 다른 스레드에서 Forms를 실행하는 경우 Application.ThreadException을 사용하여 이러한 요소를 처리 할 수 ​​있습니다 .Application.ThreadException 그러한 각 실에서

참고 URL : https://stackoverflow.com/questions/1472498/wpf-global-exception-handler



반응형