有时候,在不可复制的情况下,我的WPF应用程序崩溃了,没有任何消息。该应用程序立即关闭。

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

评论

要爱重复的链接如何回到这个问题

这个问题有更好的答案。

#1 楼

您可以处理AppDomain.UnhandledException事件

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

评论


将处理程序添加到表单构造函数中,如下所示:AppDomain.Current.UnhandledException + = ...

– Dabblernl
09-09-24 15:47

如果您创建窗口的多个实例,那就不好了...

–托马斯·莱维斯克
09-09-24 15:50

嗨,托马斯,谢谢你的回答。 Appdomain.UnHandledException对我来说很棒。

–斯科特·奥尔森(Scott Olson)
09年9月28日在9:12

可以在App.xaml.cs中添加处理程序

–乔治·比尔比利斯(George Birbilis)
2014年4月3日在20:24

@FranzKiermaier您正在使用MVVM的事实不应更改任何内容...

–托马斯·莱维斯克
20 Jan 24 '15:10

#2 楼

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




AppDomain.CurrentDomain.UnhandledException来自AppDomain中的所有线程。

Dispatcher.UnhandledException来自单个特定的UI调度程序线程。

Application.Current.DispatcherUnhandledException来自WPF应用程序中的主UI调度程序线程。

来自每个使用任务调度程序进行异步操作的AppDomain内的TaskScheduler.UnobservedTaskException

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

在#2和#3之间进行选择取决于您是否使用多个WPF线程。这是一个非常特殊的情况,如果您不确定是否要这样做,则很可能您不确定。

评论


请注意您的项目#3,我必须将.Current放在这样的Application后面:Application.Current.DispatcherUnhandledException + = ...

– Keith G
2011-3-16在20:57



@Keith G-所有这些事件都是实例成员,因此您需要根据情况将它们挂接到所需的每个对象上。

–德鲁·诺克斯(Drew Noakes)
2011-3-17在0:29

同样,我们必须对项目#1使用AppDomain.CurrentDomain.UnhandledException。

–Rev
2012年5月29日4:21



如果您现在正在使用异步任务/ TaskScheduler,我相信TaskScheduler.UnobservedTaskException + = ...也与此正交。

– DuckMaestro
2012年9月22日23:23

很高兴看到这些选项的汇编以及每个选项的说明。这是我目前正在应用程序级别记录未处理的异常的方式:gist.github.com/ronnieoverby/7568387

–罗尼·奥弗比(Ronnie Overby)
13年20月20日在18:29

#3 楼

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

评论


+1用于剪切/粘贴代码。如果您想为错误消息对话框增添趣味,则WPF扩展工具包具有消息框控件。

–阿伦M
2011年3月26日14:48

请注意,在某些情况下,设置e.Handled = true可能导致应用程序UI关闭,而进程仍在计算机上静默运行。

– qJake
2014年5月23日14:42

警告!在未处理的顶级异常中使用MessageBox.Show()可能导致进程挂起!登录或执行其他操作,但不要使用此API或显示任何UI。

–克里斯·鲍德曼(Chris Bordeman)
18/12/24在1:18

@qJake能否请您更好地描述这种情况?

– Francesco Bonizzi
20年7月16日在12:48

#4 楼

每当发生未处理的异常时,我都会在WPF应用程序中使用以下代码来显示“抱歉给您带来的不便”对话框。它显示异常消息,并询问用户是否要关闭该应用程序或忽略该异常并继续(当发生非致命异常并且用户仍可以正常继续使用该应用程序时,后一种情况很方便)。 >
在App.xaml中添加启动事件处理程序:

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


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


评论


@韦斯顿那个链接已经死了

–麦凯
15年7月29日在18:23

@McKay被删除为虚假信息:stackoverflow.com/questions/2004629/…

–weston
15年7月29日在18:26

@McKay没问题。仅供参考,它引发了一个元问题meta.stackoverflow.com/questions/300452/…

–weston
15年7月30日在14:02

只是关闭而不给用户机会保存更改仍然有点邪恶。如果这是暂时的网络故障该怎么办?

–enorl76
17-10-21在22:51

警告!在未处理的顶级异常中使用MessageBox.Show()可能导致进程挂起!登录或执行其他操作,但不要使用此API或显示任何UI。

–克里斯·鲍德曼(Chris Bordeman)
18/12/24在1:19

#5 楼

最佳答案可能是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);
    }
}


评论


可能需要包括#if DEBUG,以便Visual Studio仅在调试时才处理像正常一样的异常。很棒的解决方案。

–詹姆斯M
16年11月1日在9:02

而不是#if DEBUG,应该在RegisterGlobalExceptionHandling上使用[Conditional(“ DEBUG”)]属性。这样,您可以确保更改编译器目标时代码可以编译。

–MovGP0
16 Dec 5'在16:28

此外,最好还是在生产代码中保留全局异常的日志记录。您可以在您的依赖项注入设置中将ConditionalAttribute用于记录器的配置,而只需更改记录的详细程度即可。

–MovGP0
16 Dec 5'在16:33

#6 楼

除了上面的帖子之外,
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);
        }
    });


#7 楼

为了补充托马斯的答案,Application类还具有您可以处理的DispatcherUnhandledException事件。

#8 楼

完整的解决方案在这里

用示例代码对其进行了很好的解释。但是,请注意不要关闭应用程序。添加行
Application.Current.Shutdown();。
正常关闭应用程序。

#9 楼

如上所述,


Application.Current.DispatcherUnhandledException将无法捕获从另一个线程(然后是主线程)引发的异常。


实际情况取决于线程是如何创建的br />如果在主线程以外的其他线程上运行Forms,则需要从每个此类线程中设置Application.ThreadException

评论


从其他线程在SO处复制Hertzel Guinness的答案:app.config中的 可以防止您的辅助线程异常关闭应用”

–乔治·比尔比利斯(George Birbilis)
15年11月27日,0:09