Отчет об исключениях из приложения WPF - PullRequest
13 голосов
/ 18 мая 2011

Допустим, у меня в приложении возникло необработанное исключение, или оно по какой-то причине вылетает. Есть ли какой-то способ для меня, чтобы захватить вывод и показать диалоговое окно сообщения об ошибке, когда приложение падает.

Я имею в виду, что в фоновом режиме работает небольшая программа, единственная задача которой - прослушать ненормальный выход из основного приложения, а затем показать диалоговое окно «Отчет», в котором пользователь может отправить мне выходные данные ошибки.

Не совсем уверен, как реализовать это, или если это правильный способ сделать это.

Сообщение об ошибке было бы простой задачей, но я понятия не имею, как перехватить выходные данные необработанного исключения или получить коды выхода (я предполагаю, что программа выдаст код выхода, отличный от 0, в случае сбоя ).

Было бы здорово иметь толчок в правильном направлении.

Ответы [ 4 ]

12 голосов
/ 18 мая 2011

Ваш лучший шанс находится внутри приложения. Есть два крючка:

  • AppDomain.UnhandledException является окончательным «универсальным»
  • Application.ThreadException - это универсальный пользовательский интерфейс для исключений, которые произошли в потоках форм

Подходящее место для «всеохватывающего» зависит от семантики вашего приложения, и трудно сказать, куда вы должны его поместить без знания вашего приложения. Приложение должно также установить Application.SetUnhandledExceptionMode.

Наличие внешнего сторожа менее полезно, поскольку оно не может дать какой-либо значимой информации, почему приложение аварийно завершило работу. К тому времени, когда он обнаруживает «неожиданный» выход (откуда он знает, что он «неожиданный»?), Уже слишком поздно собирать какую-либо полезную информацию. С помощью внутреннего обработчика вы можете собрать исключение и стек и отправить их в службу анализа, например bugcollect.com , и тогда у вас будет шаг вперед в понимании только того, что произошло, но также как часто это происходит и на какое развертывание влияют (, где это происходит). Существуют и другие подобные службы, такие как exceptioneer.com или Windows Error Reporting (для этого требуется, чтобы ваш код был подписан сертификатом доверенного органа, таким как Verisign). Полагаться на службу для сбора инцидентов намного лучше, чем отправлять почту, вы не хотите просыпаться и находить 2 000 электронных писем с инцидентами в своем почтовом ящике и начинать просматривать их, чтобы понять, что произошло

И последний мир: не изобретайте колесо: уже есть много фреймворков для сбора и регистрации исключений, таких как log4net и elmah .

9 голосов
/ 18 мая 2011

Для этого вы можете использовать библиотеку NBug (также вы можете использовать пакет nuget здесь для простой установки).Просто установите пакет 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;

Теперь все необработанные исключения будут приятно перехвачены и подарены для вас, и вы получите полную информацию об исключении по электронной почте.Используйте инструмент конфигуратора, чтобы убедиться, что ваша конфигурация верна.

1 голос
/ 27 февраля 2019

Ответы, включающие событие AppDomain.UnhandledException, вероятно, предполагают, что любое необработанное исключение возникает в потоке пользовательского интерфейса WPF.Это означает, что, хотя они часто работают, они формально не безопасны для использования с операциями в других потоках.Более надежный вариант - Application.DispatcherUnhandledException, который WPF предоставляет приложениям для реализации пользовательских отчетов о необработанных исключениях в основном потоке пользовательского интерфейса, фоновых потоках пользовательского интерфейса и BackgroundWorker экземплярах.

Вероятная точка отказа с AppDomain.UnhandledException - это диалоговое окно отчета, вероятно, требующее однопотокового размещения (STA), так как и WPF, и Windows Forms являются 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 является надежным в сочетании с TPL как Task и связанные с ним классы облегчают распространение исключений обратно к вызывающей стороне.Для суммирования обработки исключений TPL , Wait() и await автоматически перебрасываются, и вызывающие абоненты, использующие другие методы синхронизации, должны проверить Task.Exception.

Однако, как *В документации 1038 * указывается, что в других случаях требуется, чтобы вызывающий WPF реализовывал распространение исключений.Возможно, наиболее распространенным из них является WPF Progress<T>, который, как ни странно, не поддерживает распространение исключений из-за его реализации IProgress<T>.Report(), хотя его единственной целью является передача информации о прогрессе от работникатемы обратно в интерфейс.Один из способов - обернуть обработчик обновления прогресса, используя подход, подобный примеру ниже.Это всего лишь эскиз;более частый опрос свойства 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);
    }
}
0 голосов
/ 18 мая 2011

Вы должны обработать событие Application.ThreadException.

...