Окно сообщения об ошибке для 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/latest2. Собрать в 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-х лет, значительно улучшив процесс исправления ошибок, поскольку о каждом падении приложения узнаешь моментально, без дополнительной нотификации от пользователя.Надеюсь это все будет кому-то полезно! Всем спасибо за внимание!