Обеспечение стабильной работы внешних компонент на DelphiПри работе с внешними компонентами рекомендуется тщательно отрабатывать исключительные ситуации - иначе 1С:Предприятие будет "обрушиваться" - порой, вместе с введенными пользователем данными - по мере наступления каких-либо проблемных ситуаций. | | Автор статьи: romix | Редакторы: pectopatop Последняя редакция №6 от 13.10.07 | История URL: http://kb.mista.ru/article.php?id=100 | |
Ключевые слова: внешняя компонента, исключение, ShowErrorLog, Exception, RaiseLastOSError, Delphi, Рихтер
Обработчик исключения позволяет возобновить работу сбоящей программы с определенной точки, чтобы факт глючности компоненты не слишком бросался в глаза (пользователи не любят, когда им что-то или кто-то бросается в глаза). Но злоупотреблять этим, маскируя от пользователя очевидные проблемы, конечно же, нельзя (подробную информацию о проблеме можно записать в текстовый лог).
Я обрабатываю исключительные ситуации так:
try
//...
//Фрагмент кода, где возможна ошибка
//...
except
on E:Exception do begin
ShowErrorLog('Ошибка чтения данных COM-порта: '+E.Message);
end;
end;
Обратите внимание на то, что "//Фрагмент кода, где возможна ошибка" может включать в себя вызовы различных процедур и функций, а они тоже вызывать другие функции. При этом весь вызываемый код считается помещенным в блок Try .. Except .. End.
Можно делать вложенные друг в друга блоки Try:
Try
//Фрагмент1 кода, где возможна ошибка. Начало
Try
//...
//Фрагмент2 кода, где возможна ошибка
//...
Except
//Обработки ошибок нет
End;
//Фрагмент1 кода, где возможна ошибка. Окончание
Except
ShowMessage('ОШИБКА!');
End;
Тогда ошибка, возникшая в самом внутреннем блоке, вызовет исключение его же (самого внутреннего) блока. Если в данном Except .. End не будут записаны какие-либо действия по обработке ошибки, то ее обработка передается на объемлющий блок Try .. Except .. End, и так далее, вплоть до самого верхнего уровня. Если и на самом верхнем уровне вашей программы не будет найдена обработка ошибки, то ею займется среда выполнения, она выдаст стандартное окно ошибки, как будто вы и не использовали вовсе блоков Try, за исключением случая, когда ваш блок Try самого верхнего уровня будет иметь вид:
try
//...
//Фрагмент кода, где возможна ошибка
//...
except
end;
То есть, если между Except и End нет никаких операторов, то обработка ошибки заключается в простом умолчании о ней во время выполнения: вы попытались сделать некоторые действия, они не осуществились, ну и Бог с ними. =)
Кроме блоков Try .. Except .. End существует другая их модификация, которая тоже может быть полезной, - блок Try .. Finally .. End. То есть вместо ключевого слова Except следует ключевое слово Finally. Тогда код записанный между Finally и End будет выполнен наоборот лишь в том случае, если код между Try и Finally выполнился успешно (т.е. без ошибок).
Метод ShowErrorLog, используемый в примере выше, выдает ошибку "красненьким" в окне сообщений 1С.
procedure AddInObject.ShowErrorLog(fMessage:WideString);
var
ErrInfo: PExcepInfo;
begin
If Trim(fMessage) = '' then Exit;
New(ErrInfo);
ErrInfo^.bstrSource := c_AddinName;
ErrInfo^.bstrDescription := fMessage;
ErrInfo^.wCode:=1006;
ErrInfo^.sCode:=E_FAIL; //генерация исключения в 1С
iError.AddError(nil, ErrInfo);
end;
Поле sCode следует установить в значение E_FAIL, если ошибка должна остановить (прервать) выполнение кода на языке 1С, либо другое значение в противном случае (эта особенность явно указана в статье на диске ИТС и не слишком явно, если вообще указана - в "Технологии создания внешних компонент" от 1С).
Многие библиотечные функции Delphi генерируют исключения при ошибках, и эти исключения, конечно же, необходимо отрабатывать. Однако, при работе с более старым инструментарием - функциями Windows API - функции не генерируют исключений, а возвращают программисту числовые значения - коды возврата. Превратить их в "нормальные" исключения можно при помощи функции RaiseLastOSError, например:
if not GetCommState(hCom,dcb) //uses Windows
then RaiseLastOSError;
Функция RaiseLastOSError анализирует код ошибки, сопоставляет ему текстовое сообщение и возвращает его же в виде исключения.
Почему исключения в данном случае удобнее? Не надо усложнять ("засорять" обработкой ошибок) основной алгоритм вашей программы. Фактически, код, "честно" контролирующий все возможные ошибки, сокращается и делается более "прозрачным" и понятным.
Кроме того, вы можете легко выйти по ошибке сразу из многих циклов или вложенных процедур. Делать это алгоритмически - пожалуй, равнозначно запутыванию своего кода и превращению его в "блюдо спагетти".
Полезные сведения об обработке исключительных ситуаций (на примере С++) пишет известный автор Дж.Рихтер в своей книге - супербестселлере "Windows для профессионалов", которая настоятельно рекомендуется к прочтению.
В среде разработки .NET программисты избавлены от необходимости использовать что-то наподобие RaiseLastOSError - все системные функции генерируют исключение при ошибках.
Систематическое применение Exception.Create("Текстовое сообщение"), или же вызов исключений конкретного типа при любых ошибках или недочетах входных данных позволит сделать программу безошибочной, легко отлаживаемой и "самотестируемой". Можно даже утверждать, что чем больше операторов вызова исключений - тем лучше. Вы заметите, как легко искать и отлаживать код, если "сторожа"-RaiseException, которые о них вам сообщают, расставлены во всех ключевых точках вашей внешней компоненты. |