Суббота, 04.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нужен форум на сайте?
Всего ответов: 90
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Разные » Пользовательский интерфейс

Автоматическая обработка ошибок

Введение

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

У всех

Типичная обработка ошибки заключается в том, чтобы обрамить блоки кода в конструкции try..except или try..finally. В общем, все по учебнику. Тем не менее, многие попросту не делают этого, так как им недосуг или обработка исключений оставляется "на потом". Когда подходит время сдачи проекта, нередко такие программисты начинают спешно латать дыры, что может дать не только положительный эффект, я имею ввиду появившуюся у программы обработку исключений, но и отрицательный. Дело в том, что обработка исключительных ситуаций тоже является частью кода, а внесение нового кода может повлечь за собой и внесение новых ошибок.

Избавиться от сложившейся ситуации не так трудно, как может показаться на первый взгляд. Почти все становится ясно, когда вспоминаешь про объект Application и его свойство OnException.

Не у всех

Как вы уже, наверное, догадались, свойство Application.OnException является глобальным обработчиком событий приложения. Перед тем как описать метод использования этого свойства, давайте договоримся, что у нас есть объект класса TgsCatcher (именно его я и описываю в данной статье), у которого есть метод TgsCatcer.Catcher, который и будет обработчиком ошибок приложения. Простейший вариант вышесказанного выглядит следующим образом:

unit gsCatcher;

//**! ----------------------------------------------------------
//**! This unit is a part of GSPackage project (Gregory Sitnin\'s
//**! Delphi Components Package).
//**! ----------------------------------------------------------
//**! You may use or redistribute this unit for your purposes
//**! while unit\'s code and this copyright notice is unchanged
//**! and exists.
//**! ----------------------------------------------------------
//**! (c) Gregory Sitnin, 2001-2002. All rights reserved.
//**! ----------------------------------------------------------

{***} interface {***}

uses Classes, SysUtils, JPEG;

type
 TgsCatcher = class(TComponent)
 private
 FEnabled: boolean;
 FGenerateScreenshot: boolean;
 FJPEGScreenshot: boolean;
 FJpegQuality: TJPEGQualityRange;
 FCollectInfo: boolean;
 Fn: TFilename;
 procedure SetEnabled(const Value: boolean);
 { Private declarations }
 protected
 { Protected declarations }
 procedure EnableCatcher;
 procedure DisableCatcher;
 public
 { Public declarations }
 constructor Create(AOwner: TComponent); override;
 destructor Destroy; override;
 procedure Catcher(Sender: TObject; E: Exception);
 published
 { Published declarations }
 property Enabled: boolean read FEnabled write SetEnabled
 default False;
 end;

procedure Register;

{***} implementation {***}

uses Windows, Forms, Dialogs, Graphics;

procedure Register;
begin
 RegisterComponents(\'Gregory Sitnin\', [TgsCatcher]);
end;

{ TgsCatcher }
constructor TgsCatcher.Create(AOwner: TComponent);
begin
 inherited;
end;

destructor TgsCatcher.Destroy;
begin
 DisableCatcher;
 inherited;
end;

procedure TgsCatcher.SetEnabled(const Value: boolean);
begin
 FEnabled := Value;
 if Enabled then EnableCatcher else DisableCatcher;
end;

procedure TgsCatcher.DisableCatcher;
begin
 Application.OnException := nil;
end;

procedure TgsCatcher.EnableCatcher;
begin
 Application.OnException := Catcher;
end;

procedure TgsCatcher.Catcher(Sender: TObject; E: Exception);
begin
 {TODO: Write some exception handling code}
end;

end.

Это компонент, который умеет подключаться и отключаться от обработчика Application.OnException при помощи установки свойства Enabled. В подключенном состоянии все возникшие в приложении исключительные ситуации перенаправляются методу TgsCatcher.Catcher, который с этими исключениями, пока, ничего не делает.

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

Реализация функций

Для начала, давайте снимем скриншот активного окна. Сделать это довольно легко. В секцию implementation подключаем модуль Graphics.

Лирическое отступление: Считается неплохим стилем, подключать модули в секцию implementation, если их информация требуется только в этой секции. Например, в секции interface я нигде не использовал ни одного класса или типа, который описан в модуле Graphics, поэтому и занес его в implementation.

После подключения вышеназванного модуля мы получили возможность работы с изображениями в формате BMP, то есть, мы можем описать объект класса TBitmap и получить в него снимок активного окна при помощи следующего вызова:

bmp := Screen.ActiveForm.GetFormImage;

Сам объект класса TBitmap создавать нет необходимости, так как он будет автоматически создан вызываемым методом GetFormImage. Теперь нам хорошо бы было полученное изображение сохранить в файл, а для этого не плохо бы было назвать файл уникальным именем. Надо заметить, что в Windows, как и во многих операционных системах есть специальный механизм создания действительно уникальных имен файлов. Однако, имена файлов на выходе этих механизмов являются, по большей части, ничего не значащими наборами символов. Но суть этой технологии в уникальности, а это она делает. Мне же хотелось, чтобы имена несли смысловую нагрузку. Было бы здорово, если бы в них было записано имя исполняемого файла, имя формы, дата и время возникновения ошибки. Решается данная задача вот таким образом (Fn - переменная строкового типа):

Fn := ExtractFilename(Application.ExeName)+\'_\'+
Screen.ActiveForm.Name+
FormatDateTime(\'_ddmmyyyy_hhnnss\',now)+
\'_debug\';

Таким образом, мы получили генератор имен файлов, создающий имена, очень похожие на "PROJECT1.EXE_Form1_22092002_171956_debug.bmp", только без расширения. Теперь, запишем изображение в файл.

bmp.SaveToFile(fn+\'.bmp\');

Итог - любое исключение в приложении вызовет автоматическую запись скриншота в файл формата BMP. Но, такой графический формат имеет одну неприятную особенность, дельфи умеет работать только с несжатыми файлами, которые благодаря этому имеют большой объем, а мне бы хотелось посылать эти файлы автоматически по e-mail, чтобы всегда быть в курсе ошибок программы.

К счастью, фирма Borland бесплатно приложила к Delphi библиотеку работы с изображениями в формате JPEG. Поищите на компакт-диске с дельфи или на своем жестком диске в каталогах дельфи файлы, начинающиеся с букв "jpeg".

Таким образом, подключаем модуль JPEG после модуля Graphics и пишем вот такой код:

procedure TgsCatcher.DoGenerateScreenshot;
var bmp: TBitmap;
 jpg: TJPEGImage;
begin
 bmp := Screen.ActiveForm.GetFormImage;
 begin
 jpg := TJPEGImage.Create;
 jpg.CompressionQuality := 100;
 jpg.Assign(bmp);
 jpg.SaveToFile(fn+\'.jpg\');
 FreeAndNil(jpg);
 end;
 FreeAndNil(bmp);
end;

>Теперь из метода Catcher достаточно просто вызвать метод DoGenerateScreenshot и автоматическое сохранение скриншота в формате JPEG с качеством 100% вам обеспечено.

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

function TgsCatcher.CollectUserName: string;
var
 uname: pchar;
 unsiz: cardinal;
begin
 uname := StrAlloc(255);
 unsiz := 254;
 GetUserName(uname,unsiz);
 if (unsiz > 0) then
 Result := string(uname) else
 Result := \'n/a\';
 StrDispose(uname);
end;

Эта функция запросит у системы имя пользователя при помощи функции API GetUserName, и вернет строку "n/a", если имя пользователя получить не удалось.

function TgsCatcher.CollectComputerName: string;
var
 cname: pchar;
 cnsiz: cardinal;
begin
 cname := StrAlloc(MAX_COMPUTERNAME_LENGTH + 1);
 cnsiz := MAX_COMPUTERNAME_LENGTH + 1;
 GetComputerName(cname,cnsiz);
 if (cnsiz > 0) then
 Result := string(cname) else
 Result := \'n/a\';
 StrDispose(cname);
end;

Эта функция аналогична функции CollectUserName, только запрашивает имя компьютера.

procedure TgsCatcher.DoCollectInfo(E: Exception);
var sl: TStringList;
begin
 sl := tstringlist.Create;
 sl.add(\'--- This report is created by automated \'+
 \'reporting system.\');
 sl.add(\'Computer name is: [\'+ComputerName+\']\');
 sl.add(\'User name is: [\'+UserName+\']\');
 sl.add(\'--- End of report ----------------------\'+
 \'-----------------\');
 sl.SaveToFile(Fn+\'.txt\');
end;

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

Заключение

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

Автор: Григорий Ситнин

Получить ссылку на материал

Категория: Пользовательский интерфейс | Добавил: Барон (19.12.2011)
Просмотров: 1135 | Теги: обработка, ошибока | Рейтинг: 0.0/0
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
Delphi.NET [3]
Kylix Delphi for Linux [9]
Советы Дельферу [6]
Хитрости в Delphi [2]
Обзор Delphi [45]
Инсталлятор [11]
Пользовательский интерфейс [18]
Примеры Delphi [93]
Функции и процедуры [15]
Разные [31]
Королевство Delphi © 2010-2024
Яндекс цитирования