Окно сообщения об ошибке для WinForms и WPF приложений

от автора


Приветствую!

В своей прошлой статье посвященной моему профайлеру для Entity Framework-a, я вкратце описал примененную мной форму для сообщения пользователю об исключительной ошибке в приложении. После оценки количества скачиваний примера кода, было решено выделить этот пример в отдельный проект, а также добавить поддержку WPF приложений.
Исходники библиотеки вместе с примерами выложены на CodePlex под MIT лицензией: https://uiexceptionhandler.codeplex.com/

Подробности под катом.

Введение

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

Что получилось

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

При клике по кнопке «Error detail information» выводиться дополнительная информация об ошибке:

Кнопка Debug позволяет подключить отладчик Visual Studio.
Кнопка «Send to Developer» отправляет письмо на почту разработчику. В случае ошибки отправки сообщения, пользователю будет предложено самому отправить лог файл разработчику на почту.
Отправленное разработчику сообщение придет в таком виде:

Использование

1. Забрать последнюю версию кода https://uiexceptionhandler.codeplex.com/SourceControl/latest
2. Собрать в Release mode.
3. Из папки «UIExceptionHandlerLibs\Deploy» подключить в проект библиотеку UIExceptionHandlerWinForms.dll в случае WinForms приложения и UIExceptionHandlerWPF.dll в случае WPF приложения.
4. Инициализировать путем вызова статического метода с рядом параметров:

UIException.Start(    string serverSmtp,     int portSmtp,     string passwdSmtp,     string userSmtp,     string programmerEmail,    string fromEmail,     string subject ) 

Как это работает

Статический метод UIException.Start подписывает метод HandleError на событие AppDomain.CurrentDomain.UnhandledException:

AppDomain.CurrentDomain.UnhandledException += (sender, e) => HandleError((Exception)e.ExceptionObject); 

Метод HandleError:

private static void HandleError(Exception exception) {     try     {         // запускаем обработчик формы и передаем ему ссылку на форму наследованную от интерфейса IErrorHandlerForm         new ErrorHandlerController(exception, new ErrorHandlerForm()).Run();     }     catch (Exception e)     {         // в случае ошибки обработки выводим сообщение с просьбой отправить лог файл разработчику на почту        MessageBox.Show("Error processing exception. Please send log file " + LogHelper.ExceptionLogFileName + " to developer: " + Settings.ProgrammerEmail + " \r\n Exception:" + e);         // сохраняем ошибку в лог файл         LogHelper.Logger.Error(e);         // спрашиваем нужно ли подключить отладчик         if (MessageBox.Show("Attach debugger? \n Only for developer!!!", "Debugging...", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)         {             Debugger.Launch();             throw;         }     }     finally     {         // обязательно завершаем приложение чтобы windows не вывело стандартное сообщение об ошибке         Environment.Exit(1);     } } 

Интерфейс IErrorHandlerForm:

public interface IErrorHandlerForm {     event Action OnSendButtonClick;     event Action OnShowErrorLinkClick;     event Action OnLogFileLinkClick;     event Action OnDebugButtonClick;      // меняет высоту формы     void SetHeight(int height);     // задает подробное сообщение об ошибке     string ExceptionInfoText { get; set; }     // получает текст из поля дополнительной информации введенной пользователем     string ExceptionDetailText { get; set; }     // email пользователя для ответа     string ReplyEmail { get; }     void ShowExceptionInfoTextBox(bool isShow);     // выводит информационное сообщение     void ShowInfoMessageBox( string text, string caption);     // выводит диалоговое сообщение     bool ShowQuestionDialog( string text, string caption);     // показывает окно в режиме диалога! необходимо чтобы приложение дожидалось закрытия окна и завершилось в finaly     void ShowViewDialog();     void UpdateContactEmail(string contactEmail); } 

В качестве библиотеки для логгирования используется NLog. Для того чтобы избежать появления лишних xml файлов, вся конфигурация Nlog-а делается в коде:

private static void ConfigureNlog() {     var config = new LoggingConfiguration();      var fileTarget = new FileTarget();     config.AddTarget("file", fileTarget);      fileTarget.Layout = @"${longdate} ${message}";     fileTarget.FileName = "${basedir}/" + ExceptionLogFileName;      var rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget);     config.LoggingRules.Add(rule2);      LogManager.Configuration = config; } 

Чтобы добиться максимальной простой интеграции в проект, я решил все используемые сборки объединить в одну библиотеку. Делается это при помощи приложения ILMerge, путем добавления скрипта в post-build событие:

if $(ConfigurationName) == Release ( "$(SolutionDir)ILMerge\ILMerge.exe" /out:"$(SolutionDir)Deploy\$(TargetFileName)" "$(TargetDir)*.dll" /target:dll /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards ) 

Послесловие

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

Надеюсь это все будет кому-то полезно!
Всем спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/222163/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *