来自WPF应用程序的异常报告

时间:2011-05-18 00:11:54

标签: c# wpf error-handling error-reporting

因此,假设我的应用程序中遇到了未处理的异常,或者由于某种原因崩溃了。 有什么方法让我捕获输出并在应用程序崩溃时显示错误报告对话框。

我在想的是在后台运行一个小程序,唯一的工作就是监听主应用程序的异常退出,然后显示“报告”对话框,用户可以选择通过电子邮件向我发送输出的错误。

不确定如何实现这一点,或者这是否是正确的方法。

报告错误消息将是一项简单的任务,但我不知道如何捕获未处理的异常的输出或获取退出代码(我假设程序在崩溃时会给出除0之外的退出代码)。

在正确的方向上轻推是很棒的。

4 个答案:

答案 0 :(得分:12)

你最好的机会在申请中。有两个钩子:

“全能”的适当位置取决于您的应用程序语义,很难说您应该知道应用程序的位置。应用程序还需要设置Application.SetUnhandledExceptionMode

拥有外部看门狗不太有用,因为它无法提供任何有意义的信息,为什么应用程序崩溃。当它检测到“意外”退出(它是如何知道“意外”?)时,为时已晚,无法收集任何有用的信息。使用内部处理程序,您可以收集异常和堆栈,并将它们提交给像bugcollect.com这样的分析服务,然后您就可以了解现在只有发生了什么,而且它发生的频率以及哪些部署受到影响( 它发生的地方)。还有其他类似的服务,例如exceptioneer.comWindows Error Reporting(这个服务要求您的代码由Verisign等受信任的授权证书签名)。依靠收集事件的服务远远优于发送邮件,您不想在收件箱中醒来并发现2k事件电子邮件并开始筛选它们以了解发生了什么

最后一个世界:不要重新发明轮子:已经有很多框架可以收集和记录异常,例如log4netelmah

答案 1 :(得分:9)

您可以使用NBug库(也可以使用nuget package here轻松安装)。只需安装NuGet包并进行如下配置:

NBug.Settings.Destination1 =
  "Type=Mail;From=me@mycompany.com;To=bugtracker@mycompany.com;SmtpServer=smtp.mycompany.com;";
AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;
Application.Current.DispatcherUnhandledException += NBug.Handler.DispatcherUnhandledException;

现在所有未处理的异常都会被很好地捕获并为您提供礼物包装,您将通过电子邮件获得异常的完整详细信息。使用配置程序工具确保配置正确。

答案 2 :(得分:1)

涉及AppDomain.UnhandledException事件的答案可能是一个隐式假设,即WPF的UI线程上引发了任何未处理的异常。这意味着,尽管它们通常可以工作,但是在其他线程上使用它们在形式上并不安全。更为可靠的选择是Application.DispatcherUnhandledException,WPF为应用程序提供了在主UI线程,后台UI线程和BackgroundWorker实例之间实现对未处理异常的自定义报告的功能。

使用AppDomain.UnhandledException的可能故障点是报告对话框可能需要单线程单元(STA),因为WPF和Windows窗体都是STA。由于线程池线程默认为多线程单元,因此在报告对话框实例化失败时,将导致Task.Run()ThreadPool.QueueUserWorkItem()IProgress<T>.Report()或许多类似的API下的异步操作的未处理异常类似于下面的异常。这会导致应用程序崩溃,而没有机会提示用户报告潜在问题。

System.InvalidOperationException: The calling thread must be STA, because many UI components require this.
   at System.Windows.Input.InputManager..ctor()
   at System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
   at System.Windows.Input.KeyboardNavigation..ctor()
   at System.Windows.FrameworkElement.FrameworkServices..ctor()
   at System.Windows.FrameworkElement.EnsureFrameworkServices()
   at System.Windows.FrameworkElement..ctor()
   at System.Windows.Controls.Control..ctor()
   at System.Windows.Window..ctor()

我对Application.DispatcherUnhandledException的经验是,它与Task一起与TPL结合使用时非常健壮,并且其关联的类有助于将异常传播回调用者。要总结the TPL's exception handlingWait()await自动重新抛出,使用其他同步方法的呼叫者应检查Task.Exception

但是,正如Application.DispatcherUnhandledException的文档所指出的,其他情况需要WPF的调用方实现异常传播。其中最常见的也许是WPF的Progress<T>,尽管它的唯一目的是将进度信息从工作线程移回到UI,但它在IProgress<T>.Report()的实现中却lacks support for propagating exceptions很奇怪。一种解决方法是使用类似于以下示例的方法包装进度更新处理程序。这只是一个草图。对Exception属性进行更频繁的轮询可能对阻止错误很有用,IDisposable的语义更强于End(),并且对于处理积压的情况可能很有用。更新并发失败的方式有所不同。

public class ExceptionPropagatingProgress<TProgress>
{
    private readonly Action<TProgress> onProgressUpdateCore;
    private readonly IProgress<TProgress> progress;

    public Exception Exception { get; private set; }

    public ExceptionPropagatingProgress(Action<TProgress> handler)
    {
        this.Exception = null;
        this.onProgressUpdateCore = handler ?? throw new ArgumentNullException(nameof(handler));
        this.progress = new Progress<TProgress>(this.OnProgressUpdate);
    }

    public void End()
    {
        if (this.Exception != null)
        {
            throw new AggregateException(this.Exception);
        }
    }

    private void OnProgressUpdate(TProgress value)
    {
        try
        {
            this.onProgressUpdateCore(value);
        }
        catch (Exception exception)
        {
            lock (this.onProgressUpdateCore)
            {
                if (this.Exception == null)
                {
                    this.Exception = exception;
                }
                else
                {
                    this.Exception = new AggregateException(this.Exception, exception);
                }
            }
        }
    }

    public void QueueProgressUpdate(TProgress value)
    {
        if (this.Exception != null)
        {
            throw new AggregateException(this.Exception);
        }

        this.progress.Report(value);
    }
}

答案 3 :(得分:0)

您应该处理Application.ThreadException事件。