Search
Write a publication
Pull to refresh

Auto Debug Tool

Reading time4 min
Views567
Итак, представим себе ситуацию. Мы написали программу, отправили ее на тестирование, тестировщик находит ошибку. Его действия – создает письмо, описыват эту ошибку, описывает шаги для воспроизведения, делает скриншот, прикрепляет скриншот к письму, отправляет вам.

Давайте облегчим его труд.

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


Класс HotKey использует библиотеку user32.dll для регистрации события на определенное сочетания клавиш (метод RegisterHotKey), если такое сочетание клавиш уже зарегистрировано, то выпадет исключение ApplicationException(«Hotkey already in use»).

В программе регистрируем событие на Ctrl-Shift-B событие OnBugHandle:

public static HotKey hotKey = new HotKey(Keys.B, HotKey.KeyModifiers.Control | HotKey.KeyModifiers.Shift, new EventHandler(OnBugHandle));

где OnBugHandle вызывает статический метод DebugTool.ProcessBug():

public static void OnBugHandle(object obj, EventArgs args)
{
DebugTool.ProcessBug();
}

Далее перейдем к классу-обертке MAPI. Этот класс позволит нам формировать письмо для отправки разработчику:

Теперь перейдем к основному классу DebugTool. Этот класс должен делать следующее:

  • Снять скриншот и записать в файл
  • Скопировать файлы логов
  • Запаковать в архив
  • Создать письмо
  • Прикрепить архив к письму


Кроме того, назначим DebugTool обрабатывать событие OnUnhandledException с записью в отдельный файл стека вызовов и самого исключения.

Создание скриншота:
private static void MakeScreenshot()
{
Bitmap Bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
Graphics Graphic = Graphics.FromImage(Bitmap);
Graphic.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0, 0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
Bitmap.Save(tempPath + "screenshot.bmp");
}


TempPath — это временная папка, ее можно запросить через Path.GetTempPath(). Туда мы и будем собирать все файлы которые нам необходимо заархивировать.

Сохранение в файл стека вызовов:

private static void SaveStack()
{
using (TextWriter textWriter = new StreamWriter(
new FileStream(tempPath + "stack.txt", FileMode.Create, FileAccess.Write),
Encoding.Unicode))
{

StackTrace st = new StackTrace(true);
for (int i = 0; i < st.FrameCount; i++)
{
StackFrame sf = st.GetFrame(i);
textWriter.WriteLine(string.Empty);
textWriter.WriteLine("Method: {0}", sf.GetMethod());
textWriter.WriteLine("Line Number: {0}", sf.GetFileLineNumber());
}
}
}

Сохранение данных об исключении (с рекурсивной записью всех внутренних исключений):

private static void ExceptionInfo(Exception ex)
{
Exception currentException = ex;
using (TextWriter textWriter = new StreamWriter(
new FileStream(tempPath + "exception.txt", FileMode.Create, FileAccess.Write),
Encoding.Unicode))
{

while (currentException != null)
{
textWriter.WriteLine(string.Empty);
textWriter.WriteLine("Message : " + currentException.Message);
textWriter.WriteLine("Stack Trace : " + currentException.StackTrace);
textWriter.WriteLine("Source : " + currentException.Source);
currentException = currentException.InnerException;
}
}
}

Теперь сама обработка ошибки. Тут мы вначале делаем скриншот, потом копируем все прикрепленные файлы во временную папку. Создаем zip-архив с помощью ZipPackage, и посылаем этот архив:

public static void ProcessBug()
{
List files = new List();
tempPath = Path.GetTempPath();
MakeScreenshot();

foreach (string BindFile in BindedFiles)
{
if (File.Exists(BindFile))
{
File.Copy(BindFile, tempPath + Path.GetFileName(BindFile), false);
files.Add(Path.GetFileName(BindFile));
}
}

files.Add(Path.GetFileName("screenshot.bmp"));
using (Package package = ZipPackage.Open(tempPath + "package.zip", FileMode.Create))
{
foreach (string fi in files)
{
Uri partUri = PackUriHelper.CreatePartUri(new Uri(fi, UriKind.Relative));
PackagePart packagePart = package.CreatePart(partUri, MediaTypeNames.Text.Plain);

using (FileStream fileStream = new FileStream(tempPath + fi, FileMode.Open, FileAccess.Read))
{
CopyStream(fileStream, packagePart.GetStream());
}
}
package.Flush();
}

SendPackage(BugMessageSubject, tempPath + "package.zip");

foreach (string fi in files)
{
File.Delete(tempPath + fi);
}
File.Delete(tempPath + "package.zip");
}


Метод SendPackage попытается вызвать окно с новым письмом, но в случае ошибки скопирует наш zip-архив на рабочий стол:

private static void SendPackage(string MessageSubject, string packageFile)
{
try
{
MAPI mapi = new MAPI();
mapi.AddRecipientTo(MessageTo);
mapi.AddAttachment(packageFile);
int sendResult = mapi.SendMailPopup(MessageSubject, MessageBody);
if (sendResult > 1)
{
string DesktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
File.Copy(packageFile, DesktopFolder + "\\bug_" + Path.GetFileNameWithoutExtension(packageFile)
+ "_" + DateTime.Now.ToString("yyyyMMddHHmm") + Path.GetExtension(packageFile), true);
MessageBox.Show("I'd copyed package.zip into desktop. Send it.");
}
}
catch (Exception ex)
{
MessageBox.Show("Can't process. Exception :" + ex.Message);
}
}


Примерно такой же будет обработка и OnUnhandledException.

Теперь проинициализируем наш класс для работы:
static void Main()
{
...
//Обработка UnhandledException
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ProcessUnhandledException);
//Прикрепим файлы протоколирования
DebugTool.BindedFiles.Add(“logs.log”);
//проинициализируем оправку для MAPI
DebugTool.MessageTo = "...@gmail.com";
DebugTool.UnhandledMessageSubject = "Unhandled exception in your application";
DebugTool.BugMessageSubject = "Bug in application";
DebugTool.MessageBody = "<Please, leave here your comment>";
}

private static void ProcessUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
DebugTool.ProcessUnhandledException(e);
}


Всё! Теперь тестеру нужно сказать, что при нажатии на Ctrl-Shift-B он может моментально уведомить нас об ошибке.

Скачать файлы
Tags:
Hubs:
Total votes 9: ↑5 and ↓4+1
Comments2

Articles