C# WPF 全局异常处理程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1472498/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 17:53:19  来源:igfitidea点击:

WPF global exception handler

c#.netwpfexceptionexception-handling

提问by Scott Olson

Sometimes, under not reproducible circumstances, my WPF application crashes without any message. The application simply close instantly.

有时,在不可重现的情况下,我的 WPF 应用程序崩溃而没有任何消息。该应用程序只需立即关闭。

Where is the best place to implement the global Try/Catch block. At least I have to implement a messagebox with: "Sorry for the inconvenience ..."

哪里是实现全局 Try/Catch 块的最佳位置。至少我必须实现一个消息框:“抱歉给您带来不便......”

采纳答案by Thomas Levesque

You can handle the AppDomain.UnhandledExceptionevent

你可以处理AppDomain.UnhandledException事件

EDIT: actually, this event is probably more adequate: Application.DispatcherUnhandledException

编辑:实际上,这个事件可能更合适: Application.DispatcherUnhandledException

回答by Drew Noakes

You can trap unhandled exceptions at different levels:

您可以在不同级别捕获未处理的异常:

  1. AppDomain.CurrentDomain.UnhandledExceptionFrom all threads in the AppDomain.
  2. Dispatcher.UnhandledExceptionFrom a single specific UI dispatcher thread.
  3. Application.Current.DispatcherUnhandledExceptionFrom the mainUI dispatcher thread in your WPF application.
  4. TaskScheduler.UnobservedTaskExceptionfrom within each AppDomain that uses a task scheduler for asynchronous operations.
  1. AppDomain.CurrentDomain.UnhandledException来自 AppDomain 中的所有线程。
  2. Dispatcher.UnhandledException来自单个特定的 UI 调度程序线程。
  3. Application.Current.DispatcherUnhandledException从WPF 应用程序中的UI 调度程序线程。
  4. TaskScheduler.UnobservedTaskException来自每个使用任务调度程序进行异步操作的 AppDomain。

You should consider what level you need to trap unhandled exceptions at.

您应该考虑在什么级别捕获未处理的异常。

Deciding between #2 and #3 depends upon whether you're using more than one WPF thread. This is quite an exotic situation and if you're unsure whether you are or not, then it's most likely that you're not.

在#2 和#3 之间做出决定取决于您是否使用了多个 WPF 线程。这是一种非常奇特的情况,如果您不确定自己是否是,那么很可能您不是。

回答by dustyburwell

To supplement Thomas's answer, the Applicationclass also has the DispatcherUnhandledExceptionevent that you can handle.

为了补充 Thomas 的回答,该Application课程还有DispatcherUnhandledException您可以处理的事件。

回答by Sergey

A quick example of code for Application.Dispatcher.UnhandledException:

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;
}

I added this code in App.xaml.cs

我在 App.xaml.cs 中添加了此代码

回答by jurev

I use the following code in my WPF apps to show a "Sorry for the inconvenience" dialog box whenever an unhandled exception occurs. It shows the exception message, and asks user whether they want to close the app or ignore the exception and continue (the latter case is convenient when a non-fatal exceptions occur and user can still normally continue to use the app).

我在 WPF 应用程序中使用以下代码在发生未处理的异常时显示“抱歉给您带来的不便”对话框。显示异常信息,询问用户是关闭应用还是忽略异常继续(后一种情况在发生非致命异常并且用户仍然可以正常继续使用应用时方便)。

In App.xaml add the Startup event handler:

在 App.xaml 中添加启动事件处理程序:

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

In App.xaml.cs code add Startup event handler function that will register the global application event handler:

在 App.xaml.cs 代码中添加将注册全局应用程序事件处理程序的启动事件处理程序函数:

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();
    } 
}

回答by Tobias Hoefer

In addition to the posts above:

除了上面的帖子:

Application.Current.DispatcherUnhandledException

will not catch exceptions that are thrown from another thread then the main thread. You have to handle those exceptions on its actual Thread. But if you want to Handle them on your global exception handler you can pass it to the main thread:

不会捕获从另一个线程然后是主线程抛出的异常。您必须在其实际线程上处理这些异常。但是如果你想在你的全局异常处理程序上处理它们,你可以将它传递给主线程:

 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);
        }
    });

回答by Jens

As mentioned above

正如刚才提到的

Application.Current.DispatcherUnhandledException will not catch exceptions that are thrown from another thread then the main thread.

Application.Current.DispatcherUnhandledException 不会捕获从另一个线程然后是主线程抛出的异常。

That actual depend on how the thread was created

这实际取决于线程的创建方式

One case that is not handled by Application.Current.DispatcherUnhandledException is System.Windows.Forms.Timer for which Application.ThreadException can be used to handle these if you run Forms on other threads than the main thread you will need to set Application.ThreadException from each such thread

Application.Current.DispatcherUnhandledException 不处理的一种情况是 System.Windows.Forms.Timer,如果您在主线程以外的其他线程上运行 Forms,则可以使用 Application.ThreadException 来处理这些情况,您需要设置 Application.ThreadException从每个这样的线程

回答by karpanai

A complete solution is here

完整的解决方案在这里

it's explained very nice with sample code. However, be careful that it does not close the application.Add the line Application.Current.Shutdown(); to gracefully close the app.

示例代码对它的解释非常好。但是,请注意它不会关闭应用程序。添加行 Application.Current.Shutdown(); 优雅地关闭应用程序。

回答by MovGP0

Best answer is probably https://stackoverflow.com/a/1472562/601990.

最佳答案可能是https://stackoverflow.com/a/1472562/601990

Here is some code that shows how to use it:

下面是一些展示如何使用它的代码:

App.xaml.cs

应用程序.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);
    }
}