Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_самостійне_опрацюв.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
1.22 Mб
Скачать

2. Протоколювання виняткових ситуацій.

Часто потрібно мати докладний матеріал для аналізу причин виникнення ВС. Розумно було б записувати всі дані про них у файл, щоб потім прогнозувати ситуацію. Такий підхід важливий для програм, які так чи інакше будуть відчужені від розробника: у разі виникнення непередбаченої ситуації це дозволить відповісти на питання "хто винуватий?" і "що робити?". В наступному прикладі запропонований варіант реалізації протоколювання ВС.

const LogName : string = 'с:\appexc.log';

procedure LogException;

var fs: TFileStream; m : word;buf : array[0..511] char;

begin

if FileExists(LogName) then m := fmOpenReadWrite else m := fmCreate;

fs := TFileStream.Create(LogName,m);

fs.Seek(0,soFromEnd);

StrPCopy(Buf,DateTimeToStr(Mow)+' . ');

ExceptionErrorMessage

(ExceptObject,ExceptAddr,@buf[StrLenfbuf)]

SizeOf(Buf)-StrLen(buf));

StrCat(Buf,#13#10);

fs.WriteBuffer (Buf, StrLer. ;buf) ) ;

fs.Free;

end;

procedure TForml.ButtonlClick(Sender: TObject);

var х,у,z: real;

begin

try

try

х:=1.0;у:=0.0;

z := х/у;

except

LogException;

raise;

end;

except

on E:EIntError do ShowMessage('ІntError');

on E:EMathError do ShowMessage('MathError');

end;

end;

Тут задачу запису інформації про ВС вирішує процедура LogException. Вона відкриває файловий потік і пише туди інформацію, що відформатувала за допомогою вже згадуваної функції ExceptionErrorMessage.

Як її параметрів виступають значення функцій Exceptobject і ExceptAddr. До сформованого рядка додається час виникнення ВС. Для кожного блоку коду, що захищається створюються дві вкладені конструкції try. .except. Перша, внутрішня – для вас; в ній ВС протоколюється і просувається далі. Зовнішня – для користувача; саме в ній проводиться аналіз типу ВС і готується повідомлення.

В Object Pascal існує і розширений варіант вживання оператора raise:

raise екземпляр об'єкту типу Exception> [at <адрес>]

Природно, об'єктний тип повинен бути породжений від Exception. Це, що в такому типі нічого не перевизначено, не таке важливо – головне, що в обробнику ІС можна відстежити саме цей тип.

ELoginError = class (Exception);

If LoginAttemptsNo > MaxAttempts then raise ELoginError.Create('Помилка реєстрації користувача');

Конструкція at <адрес> використовується для того, щоб змінити адресу, до якої прив'язується виникла ВС, в межах одного блоку обробки ВС.

3. Коди помилок у виняткових ситуаціях.

Якщо ваш додаток вже готується до продажу, якщо ви плануєте його технічну підтримку, то пора задуматися про привласнення числових кодів

Помилкам, що виникають в ньому. Повідомлення типу "Exception EZeroDivide in module MyNiceProgram at addr $0781BABO" годиться для розробника, користувача ж воно повергне в повний ступор. Якщо він подзвонить у вашу службу техпідтримки, то, швидше за все, не зможе нічого пояснити. Набагато грамотніше дати йому вже "розжовану" інформацію і, у тому числі, числовий код.

Один з шляхів рішення цієї проблеми – розміщення повідомлень про помилки в ресурсах програми. Якщо ж ви ще робите і декілька національних версій програми на різних мовах, то цей шлях – єдиний.

"Класичний" спосіб помістити текст у файл ресурсів – 3-етапний:

1. Створюється початковий файл ресурсів з розширенням res, в який поміщаються необхідні рядки з потрібними номерами.

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'с:\myfile.txt", fmOpenRead) = INVALID HANDLE VALUE then

raise EMyException.CreateRes(FileOpenError) ;

Таким чином, вирішена половина проблеми: можливим винятковим ситуаціям привласнені номери, їм у відповідність поставлений текст. Тепер про другу половину - як в обробнику ІС цей номер використовувати.

Ясно, що потрібно оголосити свій клас ІС, що включає властивість-код помилки.

EExceptionWithCode = class(Exception)

private

ErrCode : 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. Це те, що нам треба.

Щоб в програміста, resourcestring, що користується, голова не хворіла про унікальні ідентифікатори ресурсних рядків, середовище 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));

Результат обробки показаний на мал.

Результат обробки ІС типу EExceptionWithCode

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]