...

среда, 7 мая 2014 г.

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



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

В своей прошлой статье посвященной моему профайлеру для Entity Framework-a, я вкратце описал примененную мной форму для сообщения пользователю об исключительной ошибке в приложении. После оценки количества скачиваний примера кода, было решено выделить этот пример в отдельный проект, а также добавить поддержку WPF приложений.

Исходники библиотеки вместе с примерами выложены на CodePlex под MIT лицензией: http://ift.tt/1hxDcxM


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



Введение



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



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


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


Кнопка Debug позволяет подключить отладчик Visual Studio.

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

Отправленное разработчику сообщение придет в таком виде:


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



1. Забрать последнюю версию кода http://ift.tt/1s3NWc4

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-х лет, значительно улучшив процесс исправления ошибок, поскольку о каждом падении приложения узнаешь моментально, без дополнительной нотификации от пользователя.

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

Всем спасибо за внимание!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий