Читайте также: |
|
В ряде случаев можно в блоке try...except не размещать ничего. Пустой блок применяют, когда хотят просто проигнорировать возникновение некоторых ситуаций. Например, в программе предусмотрена некая обработка данных, которая может завершиться неудачно, но это не повлияет на дальнейшую работу, и пользователь может об этом не знать. В этой ситуации вполне уместно изолировать ее в пустом блоке try..except. Важно только не поместить туда больше кода, чем нужно — иначе "с водой можно выплеснуть и ребенка".
Если вы не предусмотрели блоков обработки ИС в своем коде, это не должно привести к аварийному завершению всего приложения. Все места в VCL, где управление передается коду разработчика (в том числе, конечно, все обработчики событий всех компонентов), заключены в такие блоки. Но, увы, в Borland не знают о конкретных проблемах вашей программы, и максимум, что они могут сделать для вас, — это проинформировать о типе и месте возникновения ИС. Стандартная обработка подразумевает вывод на экран панели текстового сообщения (из свойства Exception.Message) с указанием типа ошибки. Можно получить и развернутую информацию с именем модуля и адреса, где она имела место (рис. 3.2).
Рис. 3.2. Типовое окно сообщения об ошибке Для этого нужно вызвать процедуру
procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
имеющуюся в модуле SYSUTILS.PAS.
Если предусмотренной вами обработки ИС недостаточно, то можно продолжить ее дальше программно при помощи оператора raise.
Этот оператор уже встречался нам при описании создания пользовательских ИС. Там за ним следовал вызов конструктора ИС. Здесь же конструктор опущен: возбуждаться будет уже существующий объект ИС, приведший нас в блок:
...
sl:= TStringList. Create;
try
s1.LoadFromFile(AFileName);
except
sl.Free;
raise;
end;
...
В этом примере в случае возникновения исключительной ситуации созданный список строк должен быть уничтожен. Сама же обработка предоставляется "вышестоящим инстанциям".
Примечание
Есть и более простой способ присвоить обработчик событию Application.OnException. Для этого поместите на форму компонент типа TApplicationEvents (страница Additional Палитры компонентов), роль которого — предоставление "визуального" доступа к свойствам невизуального объекта TApplication. Среди его событий есть и OnException.
Но как "пощупать" переданный при исключительной ситуации объект? Обычная конструкция
on EExceptionType do...
указывает на класс объекта, но не на конкретный экземпляр. Если во время обработки требуется доступ к свойствам этого экземпляра, его нужно поименовать внутри on..do, указав перед именем класса некий идентификатор:
on EZD: EZeroDivide do EZD.Message:= 'Деление на ноль!';
Здесь возникшее исключение выступает под именем EZD. Можно изменить его свойства и отправить дальше:
var APtr: Pointer;
Forml: TForm;
try
APtr:= Forml;
with TObject(APtr) as TBitmap do;
except
on EZD: EInvalidCast do EZD.Message:=. EZD.Message + 'xa-xa!';
Raise;{ теперь обработка будет сделана в другом месте }
end;
Но как поименовать исключительную ситуацию, не попавшую ни в одну из директив on..do? Или, может быть, в вашем обработчике вообще нет on..do, а поработать с объектом надо? Описанный выше путь здесь не подходит. Для этих случаев есть пара системных функций Exceptobject и ExceptAddr. К сожалению, эти функции инициализируются только внутри конструкции try..except; в try..finally работать с объектом— исключительной ситуацией не представляется возможным.
5.19.9. Коды ошибок в исключительных ситуациях
Если приложение уже готовится к продаже, то пора задуматься о присвоении числовых кодов ошибкам, возникающим в нем. Сообщение типа "Exception EZeroDivide in module MyNiceProgram at addr $0781BABO" годится для разработчика, пользователя же оно повергнет в полный ступор. Гораздо грамотнее дать ему уже "разжеванную" информацию и, в том числе, числовой код. Один из путей решения этой проблемы — размещение сообщений об ошибках в ресурсах программы
"Классический" способ поместить текст в файл ресурсов:
1. Создается исходный файл ресурсов с расширением гс, в который помещаются необходимые строки с нужными номерами.
2. Файл обрабатывается компилятором ресурсов brcc32.exe (находится в папке bin в структуре папок Delphi). На выходе образуется одноименный файл с расширением res.
3. Файл включается в программу указанием директивы $R, например
{$R mystrings.res}.
Чтобы совместно использовать константы-номера ошибок в файле ресурсов и в коде на Delphi, вынесем их в отдельный включаемый файл с расширением inc:
const
IOError = 1000;
FileOpenError = IOError + 1;
FileSaveError = IOError + 2;
InternetError = 2000;
NoConnecticnError = InternetError + 1;
ConnectionAbortedError = InternetError + 2;
Взглянув на файл, вы увидите, что ошибки в нем сгруппированы по категориям. Советуем вам поступить так же, разделив константы категорий промежутком в 1000 или даже 10 000.
Сам файл ресурсов может выглядеть так:
#include "strids.inc" STRINGTABLE
{
FileOpenError, "File Open Error"
FileSaveError, "File Save Error"
NoConnectionError, "No Connection"
ConnectionAbortedError, "Connection Aborted"
}
Извлечь строку из ресурсов можно несколькими способами, самый простой из них — просто по числовому идентификатору, переданному в функцию Loadstr (модуль SysUtils). Код
ShowMessage(LoadStr(NoConnectionError));
покажет сообщение "NO connection".
Если же строка используется при возбуждении ИС, то место идентификатору—в перекрываемом конструкторе Exception.createRes, один из вариантов которого работает подобно функции Loadstr:
if FileOpent'c:\myfile.txt", fmOpenRead) = INVALID_HANDLE_VALUE then
raise EMyException.CreateRes(FileOpenError);
Таким образом, решена половина проблемы: возможным исключительным ситуациям присвоены номера, им в соответствие поставлен текст. Теперь о второй половине — как в обработчике ИС этот номер использовать.
Ясно, что нужно объявить свой класс ИС, включающий в себя свойство-код
ошибки.
EExceptionWithCode = class(Exception)
private
FErrCode: Integer;
public
constructor CreateResCode(ResStringRec: PResStringRec);
property ErrCode: Integer read FErrCode write FErrCode;
end;
Тогда любой обработчик сможет к нему обратиться:
if E is EExceptionWithCode then
ShowMessage('Error code: ' + IntToStr(EExceptionWithCode(E).ErrCode) +
#13*10
+ 'Error text: ' + E.Message);
Присвоить свойству ErrCode значение можно двумя способами:
1. Добавить к классу ИС еще один конструктор, содержащий код в качестве дополнительного параметра:
constructor EExceptionWithCode.CreateResCode(Ident: Integer);
begin
FErrCode:= Ident;
inherited CreateRes(Ident);
end;
2. Присвоить значение свойства в промежутке между созданием объекта ИС и его возбуждением:
var E: EExceptionWithCode; begin
E:= EExceptionWithCode.CreateRes(NoConnectionError);
E.ErrCode:= NoConnectionError;
Raise E;
end;
Но как быть тем, кто заранее не заготовил файл ресурсов, а работает со строками, описанными в PAS-файлах? Если вы используете оператор resourcestring, то помочь вам можно.
Начнем с рассмотрения ключевого слова resourcestring. Вслед за ним описываются текстовые константы. Но, в отличие от ключевого слова const, эти константы размещаются не в сегменте данных программы, а в ресурсах, и подгружаются оттуда по мере необходимости. Каждая такая константа воспринимается и обрабатывается как обычная строка. Но за каждой из них на самом деле стоит такая структура:
PResStringRec = ^TResStringRec;
TResStringRec = packed record
Module: ^Cardinal;
Identifier: Integer;
end;
Если вы еще раз посмотрите на список конструкторов объекта Exception, вы увидите, что те из них, которые работают с ресурсами, имеют перегружаемую версию с параметром типа pResstringRec, они предназначены для строк из resourcestring. А взглянув на приведенную выше структуру, вы увидите в ней поле identifier. Среда Delphi берет на себя заботу об уникальных идентификаторах ресурсных строк.. Номера назначаются компилятором, начиная от 65 535 (SmallInt (-D) и ниже (если рассматривать номер как тип (SmallInt, то выше): 65 534, 65 533 и т. п. Сначала в этом списке идут несколько сотен resourcestring-констант, описанных в VCL (из модулей, чье имя заканчивается на const или consts: sysconst, DBConsts и т. п.). Затем очередь доходит до пользовательских констант (рис. 3.3).
С одной стороны, отсутствие лишних забот — это большой плюс; с другой стороны, разработчик не может задать строкам те номера, какие хочет.
Все остальное почти ничем не отличается от работы с "самодельными" ресурсами. Так выглядит перегружаемая версия конструктора нашего объекта EExceptionWithCode:
constructor EExceptionWithCode.CreateResCode(ResStringRec:PResStringRec);
begin
FErrCode:= ResStringRec^.Identifier;
inherited CreateRes(ResStringRec);
end;
А так — возбуждение самой ИС:
resourcestring sErrorl = 'Error 1';
Raise EExceptionWithCode.CreateResCode
(PResStringRec(@sErrorl));
Результат обработки показан на рис. 3.3.
Рис. 3.3. Результат обработки ИС типа EExceptionWithCode
Дата добавления: 2014-12-18; просмотров: 78 | Поможем написать вашу работу | Нарушение авторских прав |