В Delphi, как и в других языках программирования, есть механизмы вызова, отлова и обработки исключений. В этой статье опишу основные инструменты работы с исключениями.
Отлов и обработка исключений
Для защиты кода от прерывания из-за ошибки существуют специальные блоки: try-except-end и try-finally-end. Различие блоков состоит в том, что код между словами except и end выполняется в случае возникновении ошибки между словами try и except, и код в блоке finally выполняется в любом случае, даже если в блоке try произошла ошибка.
Блок try-except-end используется для отлова непредвиденных и не декларируемых ошибок. В части except-end можно просто написать код, который будет отработан при возникновении любой исключительной ситуации, или описать реакции на различные классы ошибок (некоторые классы ошибок описаны тут).
try
//Защищенный код
except
//Что делать, при возникновении ошибки
end;
try
//Защищенный код
except
on E:EDivByZero do
begin
//Если было деление на ноль
end;
on E: Exception do
begin
//Любая другая ошибка
end;
end;
Блок try-finally-end чаще всего используется для защищенной очистки памяти, например:
//Начиная с версии 10.3.3 доступно inline объявление переменных
var StrList := TStringList.Create;
try
//Работа с StrList
finally
FreeAdnNil(StrList);
end;
Если вдруг при работе со списком StrList возникнет ошибка, программа автоматически перейдет к коду FreeAdnNil(StrList), очистив память. Без использования блока try-finally-end есть риск обрасти утечками памяти.
Иногда бывает необходимость и обработать ошибки и очистить память, т.е. скомбинировать два типа блоков. В Delphi не существует единого блока try-except-finally, как в некоторых других языках, поэтому данную задачу можно решить следующим способом:
var StrList := TStringList.Create;
try
try
//Программный код
except on E: Exception do
//Обработка ошибки
end;
finally
//Очистка памяти
FreeAndNil(StrList);
end;
Если вы уверены, что в блоке except при обработке ошибки возникновение ошибки исключено, то данный код можно сократить:
var StrList := TStringList.Create;
try
//Программный код
except on E: Exception do
//Обработка ошибки
end;
//Очистка памяти
FreeAndNil(StrList);
Поднятие (вызов) исключения
Иногда требуется вызвать спое собственное исключение, сообщая о той или иной ошибке. Для этого существует ключевое слово raise:
raise Exception.Create(‘Error Message’);
Вместо класса Exception может быть применен любой класс ошибки.
Обработка глобальных исключений
У класса Application есть событие OnException, которое срабатывает при возникновении исключений в любом месте основного потока программы. Его можно использовать, например, для логирования всех ошибок:
procedure TForm1.LogExceptoin(Sender: TObject; E: Exception);
begin
Memo1.Lines.Add(E.Message);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := LogExceptoin;
end;
I’m asking for Delphi native, not Prism(net).
This is my code:
raise Exception.Create('some test');
Undeclared identifier «Exception».
Where’s the problem, how do I throw/raise exceptions?
w5m
2,2863 gold badges34 silver badges46 bronze badges
asked Jul 13, 2009 at 10:08
Ivan ProdanovIvan Prodanov
34.7k78 gold badges176 silver badges248 bronze badges
The exception class «Exception» is declared in the unit SysUtils. So you must add «SysUtils» to your uses-clause.
uses
SysUtils;
procedure RaiseMyException;
begin
raise Exception.Create('Hallo World!');
end;
answered Jul 13, 2009 at 10:12
3
Remember to add SysUtils
to your uses
units.
I also suggest below a nice way to keep track of categories, formats of messages and meaning of exception:
Type TMyException=class
public
class procedure RaiseError1(param:integer);
class procedure RaiseError2(param1,param2:integer);
class procedure RaiseError3(param:string);
end;
implementation
class procedure TMyException.RaiseError1(param:integer);
begin
raise Exception.create(format('This is an exception with param %d',[param]));
end;
//declare here other RaiseErrorX
A simple way of using this is:
TMyException.RaiseError1(123);
w5m
2,2863 gold badges34 silver badges46 bronze badges
answered Jul 14, 2009 at 15:27
3
You may need to add sysutils to the uses clause, it is not built in and is optional according to Delphi in a nutshell.
answered Jul 13, 2009 at 10:11
RobSRobS
3,80727 silver badges34 bronze badges
You are using SysUtils aren’t you? Exception is declared in there IIRC.
answered Jul 13, 2009 at 10:11
WillWill
74k40 gold badges170 silver badges246 bronze badges
Обработка исключительных ситуаций в DelphiСодержание
Структурная обработка исключительных ситуаций Модель исключительных ситуаций в Delphi Синтаксис обработки исключительных ситуаций Примеры обработки исключительных ситуаций Вызов исключительной ситуации Доступ к экземпляру объекта exception Предопределенные обработчики исключительных ситуаций Исключения, возникающие при работе с базами данных Заключение ОбзорС целью поддержки структурной обработки исключительных ситуаций (exception) в Delphi введены новые расширения языка Pascal. В данной статье будет дано описание того, что из себя представляет такая обработка, почему она полезна, будут приведены соответствующий синтаксис Object Pascal и примеры использования исключительных ситуаций в Delphi. Структурная обработка исключительных ситуацийСтруктурная обработка исключительных ситуаций — это система, позволяющая программисту при возникновении ошибки (исключительной ситуации) связаться с кодом программы, подготовленным для обработки такой ошибки. Это выполняется с помощью языковых конструкций, которые как бы «охраняют» фрагмент кода программы и определяют обработчики ошибок, которые будут вызываться, если что-то пойдет не так в «охраняемом» участке кода. В данном случае понятие исключительной ситуации относится к языку и не нужно его путать с системными исключительными ситуациями (hardware exceptions), такими как General Protection Fault. Эти исключительные ситуации обычно используют прерывания и особые состояния «железа» для обработки критичной системной ошибки; исключительные ситуации в Delphi же независимы от «железа», не используют прерываний и используются для обработки ошибочных состояний, с которыми подпрограмма не готова иметь дело. Системные исключительные ситуации, конечно, могут быть перехвачены и преобразованы в языковые исключительные ситуации, но это только одно из применений языковых исключительных ситуаций. При традиционной обработке ошибок, ошибки, обнаруженные в процедуре обычно передаются наружу (в вызывавшую процедуру) в виде возвращаемого значения функции, параметров или глобальных переменных (флажков). Каждая вызывающая процедура должна проверять результат вызова на наличие ошибки и выполнять соответствующие действия. Часто, это просто выход еще выше, в более верхнюю вызывающую процедуру и т.д. : функция A вызывает B, B вызывает C, C обнаруживает ошибку и возвращает код ошибки в B, B проверяет возвращаемый код, видит, что возникла ошибка и возвращает код ошибки в A, A проверяет возвращаемый код и выдает сообщение об ошибке либо решает сделать что-нибудь еще, раз первая попытка не удалась. Такая «пожарная бригада» для обработки ошибок трудоемка, требует написания большого количества кода в котором можно легко ошибиться и который трудно отлаживать. Одна ошибка в коде программы или переприсвоение в цепочке возвращаемых значений может привести к тому, что нельзя будет связать ошибочное состояние с положением дел во внешнем мире. Результатом будет ненормальное поведение программы, потеря данных или ресурсов, или крах системы. Структурная обработка исключительной ситуации замещает ручную обработку ошибок автоматической, сгенерированной компилятором системой уведомления. В приведенном выше примере, процедура A установила бы «охрану» со связанным обработчиком ошибки на фрагмент кода, в котором вызывается B. B просто вызывает C. Когда C обнаруживает ошибку, то создает (raise) исключительную ситуацию. Специальный код, сгенерированный компилятором и встроенный в Run-Time Library (RTL) начинает поиск обработчика данной исключительной ситуации. При поиске «защищенного» участка кода используется информация, сохраненная в стеке. В процедурах C и B нет такого участка, а в A — есть. Если один из обработчиков ошибок, которые используются в A, подходит по типу для возникшей в C исключительной ситуации, то программа переходит на его выполнение. При этом, область стека, используемая в B и C, очищается; выполнение этих процедур прекращается. Если в A нет подходящего обработчика, то поиск продолжается в более верхнем уровне, и так может идти, пока поиск не достигнет подходящего обработчика ошибок среди используемых по умолчанию обработчиков в RTL. Обработчики ошибок из RTL только показывают сообщение об ошибке и форсированно прекращают выполнение программы. Любая исключительная ситуация, которая осталась необработанной, приведет к прекращению выполнения приложения. Без проверки возвращаемого кода после каждого вызова подпрограммы, код программы должен быть более простым, а скомпилированный код — более быстрым. При наличии исключительных ситуаций подпрограмма B не должна содержать дополнительный код для проверки возвращаемого результата и передачи его в A. B ничего не должна делать для передачи исключительной ситуации, возникшей в C, в процедуру A — встроенная система обработки исключительных ситуаций делает всю работу. Данная система называется структурной, поскольку обработка ошибок определяется областью «защищенного» кода; такие области могут быть вложенными. Выполнение программы не может перейти на произвольный участок кода; выполнение программы может перейти только на обработчик исключительной ситуации активной программы. Модель исключительных ситуаций в DelphiМодель исключительных ситуаций в Object Pascal является невозобновляемой(non-resumable). При возникновении исключительной ситуации Вы уже не сможете вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая(resumable) модель). Невозобновляемые исключительные ситуации разрушают стек, поскольку они сканируют его в поисках обработчика; в возобновляемой модели необходимо сохранять стек, состояние регистров процессора в точке возникновения ошибки и выполнять поиск обработчика и его выполнение в отдельном стеке. Возобновляемую систему обработки исключительных ситуаций гораздо труднее создать и применять, нежели невозобновляемую. Синтаксис обработки исключительных ситуацийТеперь, когда мы рассмотрели, что такое исключительные ситуации, давайте дадим ясную картину, как они применяются. Новое ключевое слово, добавленное в язык Object Pascal — try. Оно используется для обозначения первой части защищенного участка кода. Существует два типа защищенных участков:
Первый тип используется для обработки исключительных ситуаций. Его синтаксис: try Statement 1; Statement 2; ... except on Exception1 do Statement; on Exception2 do Statement; ... else Statements; {default exception-handler} end; Для уверенности в том, что ресурсы, занятые вашим приложением, освободятся в любом случае, Вы можете использовать конструкцию второго типа. Код, расположенный в части finally, выполняется в любом случае, даже если возникает исключительная ситуация. Соответствующий синтаксис: try Statement1; Statement2; ... finally Statements; { These statements always execute } end; Примеры обработки исключительных ситуацийНиже приведены процедуры A,B и C, обсуждавшиеся ранее, воплощенные в новом синтаксисе Object Pascal: type ESampleError = class(Exception); var ErrorCondition: Boolean; procedure C; begin writeln('Enter C'); if (ErrorCondition) then begin writeln('Raising exception in C'); raise ESampleError.Create('Error!'); end; writeln('Exit C'); end; procedure B; begin writeln('enter B'); C; writeln('exit B'); end; procedure A; begin writeln('Enter A'); try writeln('Enter A''s try block'); B; writeln('After B call'); except on ESampleError do writeln('Inside A''s ESampleError handler'); on ESomethingElse do writeln('Inside A''s ESomethingElse handler'); end; writeln('Exit A'); end; begin writeln('begin main'); ErrorCondition := True; A; writeln('end main'); end. При ErrorCondition = True программа выдаст: begin main Enter A Enter A's try block enter B Enter C Raising exception in C Inside A's ESampleError handler Exit A end main Возможно вас удивила декларация типа ‘ESampleError =class’ вместо ‘=object’; это еще одно новое расширение языка. Delphi вводит новую модель объектов, доступную через декларацию типа ‘=class’. Описание новой объектной модели дается в других уроках. Здесь же достаточно сказать, что исключительные ситуации (exceptions) являются классами, частью новой объектной модели. Процедура C проверяет наличие ошибки (в нашем случае это значение глобальной переменной) и, если она есть (а это так), C вызывает(raise) исключительную ситуацию класса ESampleError. Процедура A помещает часть кода в блок try..except. Первая часть этого блока содержит часть кода, аналогично конструкции begin..end. Эта часть кода завершается ключевым словом except, далее следует один или более обработчиков исключительных ситуаций on xxxx do yyyy, далее может быть включен необязательный блок else, вся конструкция заканчивается end;. В конструкции, назначающей определенную обработку для конкретной исключительной ситуации (on xxxx do yyyy), после резервного слова on указывается класс исключительной ситуации, а после do следует собственно код обработки данной ошибки. Если возникшая исключительная ситуация подходит по типу к указанному после on, то выполнение программы переходит сюда (на код после do). Исключительная ситуация подходит в том случае, если она того же класса, что указан в on, либо является его потомком. Например, в случае on EFileNotFound обрабатываться будет ситуация, когда файл не найден. А в случае on EFileIO — все ошибки при работе с файлами, в том числе и предыдущая ситуация. В блоке else обрабатываются все ошибки, не обработанные до этого. Приведенные в примере процедуры содержат код (строка с writeln), который отображает путь выполнения программы. Когда C вызывает exception, программа сразу переходит на обработчик ошибок в процедуре A, игнорируя оставшуюся часть кода в процедурах B и C. После того, как найден подходящий обработчик ошибки, поиск оканчивается. После выполнения кода обработчика, программа продолжает выполняться с оператора, стоящего после слова end блока try..except (в примере — writeln(‘Exit A’)). Конструкция try..except подходит, если известно, какой тип ошибок нужно обрабатывать в конкретной ситуации. Но что делать, если требуется выполнить некоторые действия в любом случае, произошла ошибка или нет? Это тот случай, когда понадобится конструкция try..finally. Рассмотрим модифицированную процедуру B: procedure NewB; var P: Pointer; begin writeln('enter B'); GetMem(P, 1000); C; FreeMem(P, 1000); writeln('exit B'); end; Если C вызывает исключительную ситуацию, то программа уже не возвращается в процедуру B. А что же с теми 1000 байтами памяти, захваченными в B? Строка FreeMem(P,1000) не выполнится и Вы потеряете кусок памяти. Как это исправить? Нужно ненавязчиво включить процедуру B в процесс, например: procedure NewB; var P: Pointer; begin writeln('enter NewB'); GetMem(P, 1000); try writeln('enter NewB''s try block'); C; writeln('end of NewB''s try block'); finally writeln('inside NewB''s finally block'); FreeMem(P, 1000); end; writeln('exit NewB'); end; Если в A поместить вызов NewB вместо B, то программа выведет сообщения следующим образом: begin main Enter A Enter A's try block enter NewB enter NewB's try block Enter C Raising exception in C inside NewB's finally block Inside A's ESampleError handler Exit A end main Код в блоке finally выполнится при любой ошибке, возникшей в соответствующем блоке try. Он же выполнится и в том случае, если ошибки не возникло. В любом случае память будет освобождена. Если возникла ошибка, то сначала выполняется блок finally, затем начинается поиск подходящего обработчика. В штатной ситуации, после блока finally программа переходит на следующее предложение после блока. Почему вызов GetMem не помещен внутрь блока try? Этот вызов может окончиться неудачно и вызвать exception EOutOfMemory. Если это произошло, то FreeMem попытается освободить память, которая не была распределена. Когда мы размещаем GetMem вне защищаемого участка, то предполагаем, что B сможет получить нужное количество памяти, а если нет, то более верхняя процедура получит уведомление EOutOfMemory. А что, если требуется в B распределить 4 области памяти по схеме все-или-ничего? Если первые две попытки удались, а третья провалилась, то как освободить захваченную область память? Можно так: procedure NewB; var p,q,r,s: Pointer; begin writeln('enter B'); P := nil; Q := nil; R := nil; S := nil; try writeln('enter B''s try block'); GetMem(P, 1000); GetMem(Q, 1000); GetMem(R, 1000); GetMem(S, 1000); C; writeln('end of B''s try block'); finally writeln('inside B''s finally block'); if P <> nil then FreeMem(P, 1000); if Q <> nil then FreeMem(Q, 1000); if R <> nil then FreeMem(R, 1000); if S <> nil then FreeMem(S, 1000); end; writeln('exit B'); end; Установив сперва указатели в NIL, далее можно определить, успешно ли прошел вызов GetMem. Оба типа конструкции try можно использовать в любом месте, допускается вложенность любой глубины. Исключительную ситуацию можно вызывать внутри обработчика ошибки, конструкцию try можно использовать внутри обработчика исключительной ситуации. Вызов исключительной ситуацииВ процедуре C из примера мы уже могли видеть, как должна поступать программа при обнаружении состояния ошибки — она вызывает исключительную ситуацию: raise ESampleError.Create('Error!'); После ключевого слова raise следует код, аналогичный тому, что используется для создания нового экземпляра класса. Действительно, в момент вызова исключительной ситуации создается экземпляр указанного класса; данный экземпляр существует до момента окончания обработки исключительной ситуации и затем автоматически уничтожается. Вся информация, которую нужно сообщить в обработчик ошибки передается в объект через его конструктор в момент создания. Почти все существующие классы исключительных ситуаций являются наследниками базового класса Exception и не содержат новых свойств или методов. Класс Exception имеет несколько конструкторов, какой из них конкретно использовать — зависит от задачи. Описание класса Exception можно найти в on-line Help. Доступ к экземпляру объекта exceptionДо сих пор мы рассматривали механизмы защиты кода и ресурсов, логику работы программы в исключительной ситуации. Теперь нужно немного разобраться с тем, как же обрабатывать возникшую ошибку. А точнее, как получить дополнительную информацию о коде ошибки, текст сообщения и т.п. Как уже говорилось, при вызове исключительной ситуации (raise) автоматически создается экземпляр соответствующего класса, который и содержит информацию об ошибке. Весь вопрос в том, как в обработчике данной ситуации получить доступ к этому объекту. Рассмотрим модифицированную процедуру A в нашем примере: procedure NewA; begin writeln('Enter A'); try writeln('Enter A''s try block'); B; writeln('After B call'); except on E: ESampleError do writeln(E.Message); on ESomethingElse do writeln('Inside A''s ESomethingElse handler'); end; writeln('Exit A'); end; Здесь все изменения внесены в строку on ESE: ESampleError do writeln(ESE.Message); Пример демонстрирует еще одно новшество в языке Object Pascal — создание локальной переменной. В нашем примере локальной переменной является ESE — это тот самый экземпляр класса ESampleError, который был создан в процедуре C в момент вызова исключительного состояния. Переменная ESE доступна только внутри блока do. Свойство Message объекта ESE содержит сообщение, которое было передано в конструктор Create в процедуре C. Есть еще один способ доступа к экземпляру exception — использовать функцию ExceptionObject: on ESampleError do writeln(ESampleError(ExceptionObject).Message); Предопределенные обработчики исключительных ситуацийНиже Вы найдете справочную информацию по предопределенным исключениям, необходимую для профессионального программирования в Delphi.
procedure Abort; begin raise EAbort.CreateRes(SOperationAborted) at ReturnAddr; end;
Исключения, возникающие при работе с базами данныхDelphi, обладая прекрасными средствами доступа к данным, основывающимися на интерфейсе IDAPI, реализованной в виде библиотеки Borland Database Engine (BDE), включает ряд обработчиков исключительных ситуаций для регистрации ошибок в компонентах VCL работающим с БД. Дадим краткую характеристику основным из них:
repeat {пока не откроем таблицу или не нажмем кнопку Cancel} try Table1.Active := True; {Пытаемся открыть таблицу} Break; { Если нет ошибки - прерваем цикл} except on EDatabaseError do {Если нажата OK - повторяем попытку открытия Table1} if MessageDlg('Не могу открыть Table1', mtError, [mbOK, mbCancel], 0) <> mrOK then raise; end; until False;
EDBEngineError = class(EDatabaseError) private FErrors: TList; function GetError(Index: Integer): TDBError; function GetErrorCount: Integer; public constructor Create(ErrorCode: DBIResult); destructor Destroy; property ErrorCount: Integer; property Errors[Index: Integer]: TDBError; end; Особенно важны два свойства класса EDBEngineError : Errors ЗаключениеДанный урок должен был дать вам достаточно информации для того, чтобы начать исследование того, как Вы можете использовать систему обработки исключительных ситуаций в вашей программе. Вы, конечно, можете обрабатывать ошибки и без привлечения этой системы; но с ней Вы получите лучшие результаты с меньшими усилиями. Назад | Содержание |
Go Up to Classes and Objects Index
This topic covers the following material:
- A conceptual overview of exceptions and exception handling
- Declaring exception types
- Raising and handling exceptions
Contents
- 1 About Exceptions
- 2 When To Use Exceptions
- 3 Declaring Exception Types
- 4 Raising and Handling Exceptions
- 4.1 Try…except Statements
- 4.2 Re-raising Exceptions
- 4.3 Nested Exceptions
- 4.4 Try…finally Statements
- 5 Standard Exception Classes and Routines
- 6 See Also
About Exceptions
An exception is raised when an error or other event interrupts normal execution of a program. The exception transfers control to an exception handler, which allows you to separate normal program logic from error-handling. Because exceptions are objects, they can be grouped into hierarchies using inheritance, and new exceptions can be introduced without affecting existing code. An exception can carry information, such as an error message, from the point where it is raised to the point where it is handled.
When an application uses the SysUtils unit, most runtime errors are automatically converted into exceptions. Many errors that would otherwise terminate an application — such as insufficient memory, division by zero, and general protection faults — can be caught and handled.
When To Use Exceptions
Exceptions provide an elegant way to trap runtime errors without halting the program and without awkward conditional statements. The requirements imposed by exception handling semantics impose a code/data size and runtime performance penalty. While it is possible to raise exceptions for almost any reason, and to protect almost any block of code by wrapping it in a try…except or try…finally statement, in practice these tools are best reserved for special situations.
Exception handling is appropriate for errors whose chances of occurring are low or difficult to assess, but whose consequences are likely to be catastrophic (such as crashing the application); for error conditions that are complicated or difficult to test for in if…then statements; and when you need to respond to exceptions raised by the operating system or by routines whose source code you don’t control. Exceptions are commonly used for hardware, memory, I/O, and operating-system errors.
Conditional statements are often the best way to test for errors. For example, suppose you want to make sure that a file exists before trying to open it. You could do it this way:
try AssignFile(F, FileName); Reset(F); // raises an EInOutError exception if file is not found except on Exception do ... end;
But you could also avoid the overhead of exception handling by using:
if FileExists(FileName) then // returns False if file is not found; raises no exception begin AssignFile(F, FileName); Reset(F); end;
Assertions provide another way of testing a Boolean condition anywhere in your source code. When an Assert statement fails, the program either halts with a runtime error or (if it uses the SysUtils unit) raises an SysUtils.EAssertionFailed exception. Assertions should be used only to test for conditions that you do not expect to occur.
Declaring Exception Types
Exception types are declared just like other classes. In fact, it is possible to use an instance of any class as an exception, but it is recommended that exceptions be derived from the SysUtils.Exception class defined in SysUtils.
You can group exceptions into families using inheritance. For example, the following declarations in SysUtils define a family of exception types for math errors:
type EMathError = class(Exception); EInvalidOp = class(EMathError); EZeroDivide = class(EMathError); EOverflow = class(EMathError); EUnderflow = class(EMathError);
Given these declarations, you can define a single SysUtils.EMathError exception handler that also handles SysUtils.EInvalidOp, SysUtils.EZeroDivide, SysUtils.Overflow, and SysUtils.EUnderflow.
Exception classes sometimes define fields, methods, or properties that convey additional information about the error. For example:
type EInOutError = class(Exception) ErrorCode: Integer; end;
Raising and Handling Exceptions
To raise an exception object, use an instance of the exception class with a raise statement. For example:
raise EMathError.Create;
In general, the form of a raise statement is
raise object at address
where object and at address are both optional. When an address is specified, it can be any expression that evaluates to a pointer type, but is usually a pointer to a procedure or function. For example:
raise Exception.Create('Missing parameter') at @MyFunction;
Use this option to raise the exception from an earlier point in the stack than the one where the error actually occurred.
When an exception is raised — that is, referenced in a raise statement — it is governed by special exception-handling logic. A raise statement never returns control in the normal way. Instead, it transfers control to the innermost exception handler that can handle exceptions of the given class. (The innermost handler is the one whose try…except block was most recently entered but has not yet exited.)
For example, the function below converts a string to an integer, raising an SysUtils.ERangeError exception if the resulting value is outside a specified range.
function StrToIntRange(const S: string; Min, Max: Longint): Longint; begin Result := StrToInt(S); // StrToInt is declared in SysUtils if (Result < Min) or (Result > Max) then raise ERangeError.CreateFmt('%d is not within the valid range of %d..%d', [Result, Min, Max]); end;
Notice the CreateFmt method called in the raise statement. SysUtils.Exception and its descendants have special constructors that provide alternative ways to create exception messages and context IDs.
A raised exception is destroyed automatically after it is handled. Never attempt to destroy a raised exception manually.
Note: Raising an exception in the initialization section of a unit may not produce the intended result. Normal exception support comes from the SysUtils unit, which must be initialized before such support is available. If an exception occurs during initialization, all initialized units — including SysUtils — are finalized and the exception is re-raised. Then the exception is caught and handled, usually by interrupting the program. Similarly, raising an exception in the finalization section of a unit may not lead to the intended result if SysUtils has already been finalized when the exception has been raised.
Try…except Statements
Exceptions are handled within try…except statements. For example:
try X := Y/Z; except on EZeroDivide do HandleZeroDivide; end;
This statement attempts to divide Y by Z, but calls a routine named HandleZeroDivide if an SysUtils.EZeroDivide exception is raised.
The syntax of a try…except statement is:
try statements except exceptionBlock end
where statements is a sequence of statements (delimited by semicolons) and exceptionBlock is either:
- another sequence of statements or
- a sequence of exception handlers, optionally followed by
else statements
An exception handler has the form:
on identifier: type do statement
where identifier: is optional (if included, identifier can be any valid identifier), type is a type used to represent exceptions, and statement is any statement.
A try…except statement executes the statements in the initial statements list. If no exceptions are raised, the exception block (exceptionBlock) is ignored and control passes to the next part of the program.
If an exception is raised during execution of the initial statements list, either by a raise statement in the statements list or by a procedure or function called from the statements list, an attempt is made to ‘handle’ the exception:
- If any of the handlers in the exception block matches the exception, control passes to the first such handler. An exception handler ‘matches’ an exception just in case the type in the handler is the class of the exception or an ancestor of that class.
- If no such handler is found, control passes to the statement in the else clause, if there is one.
- If the exception block is just a sequence of statements without any exception handlers, control passes to the first statement in the list.
If none of the conditions above is satisfied, the search continues in the exception block of the next-most-recently entered try…except statement that has not yet exited. If no appropriate handler, else clause, or statement list is found there, the search propagates to the next-most-recently entered try…except statement, and so forth. If the outermost try…except statement is reached and the exception is still not handled, the program terminates.
When an exception is handled, the stack is traced back to the procedure or function containing the try…except statement where the handling occurs, and control is transferred to the executed exception handler, else clause, or statement list. This process discards all procedure and function calls that occurred after entering the try…except statement where the exception is handled. The exception object is then automatically destroyed through a call to its Destroy destructor and control is passed to the statement following the try…except statement. (If a call to the Exit, Break, or Continue standard procedure causes control to leave the exception handler, the exception object is still automatically destroyed.)
In the example below, the first exception handler handles division-by-zero exceptions, the second one handles overflow exceptions, and the final one handles all other math exceptions. SysUtils.EMathError appears last in the exception block because it is the ancestor of the other two exception classes; if it appeared first, the other two handlers would never be invoked:
try ... except on EZeroDivide do HandleZeroDivide; on EOverflow do HandleOverflow; on EMathError do HandleMathError; end;
An exception handler can specify an identifier before the name of the exception class. This declares the identifier to represent the exception object during execution of the statement that follows on…do. The scope of the identifier is limited to that statement. For example:
try ... except on E: Exception do ErrorDialog(E.Message, E.HelpContext); end;
If the exception block specifies an else clause, the else clause handles any exceptions that aren’t handled by the block’s exception handlers. For example:
try ... except on EZeroDivide do HandleZeroDivide; on EOverflow do HandleOverflow; on EMathError do HandleMathError; else HandleAllOthers; end;
Here, the else clause handles any exception that isn’t an SysUtils.EMathError.
An exception block that contains no exception handlers, but instead consists only of a list of statements, handles all exceptions. For example:
try ... except HandleException; end;
Here, the HandleException routine handles any exception that occurs as a result of executing the statements between try and except.
Re-raising Exceptions
When the reserved word raise occurs in an exception block without an object reference following it, it raises whatever exception is handled by the block. This allows an exception handler to respond to an error in a limited way and then re-raise the exception. Re-raising is useful when a procedure or function has to clean up after an exception occurs but cannot fully handle the exception.
For example, the GetFileList function allocates a TStringList object and fills it with file names matching a specified search path:
function GetFileList(const Path: string): TStringList; var I: Integer; SearchRec: TSearchRec; begin Result := TStringList.Create; try I := FindFirst(Path, 0, SearchRec); while I = 0 do begin Result.Add(SearchRec.Name); I := FindNext(SearchRec); end; except Result.Free; raise; end; end;
GetFileList creates a TStringList object, then uses the FindFirst and FindNext functions (defined in SysUtils) to initialize it. If the initialization fails — for example because the search path is invalid, or because there is not enough memory to fill in the string list — GetFileList needs to dispose of the new string list, since the caller does not yet know of its existence. For this reason, initialization of the string list is performed in a try…except statement. If an exception occurs, the statement’s exception block disposes of the string list, then re-raises the exception.
Nested Exceptions
Code executed in an exception handler can itself raise and handle exceptions. As long as these exceptions are also handled within the exception handler, they do not affect the original exception. However, once an exception raised in an exception handler propagates beyond that handler, the original exception is lost. This is illustrated by the Tan function below:
type ETrigError = class(EMathError); function Tan(X: Extended): Extended; begin try Result := Sin(X) / Cos(X); except on EMathError do raise ETrigError.Create('Invalid argument to Tan'); end; end;
If an SysUtils.EMathError exception occurs during execution of Tan, the exception handler raises an ETrigError. Since Tan does not provide a handler for ETrigError, the exception propagates beyond the original exception handler, causing the SysUtils.EMathError exception to be destroyed. To the caller, it appears as if the Tan function has raised an ETrigError exception.
Try…finally Statements
Sometimes you want to ensure that specific parts of an operation are completed, whether or not the operation is interrupted by an exception. For example, when a routine acquires control of a resource, it is often important that the resource be released, regardless of whether the routine terminates normally. In these situations, you can use a try…finally statement.
The following example shows how code that opens and processes a file can ensure that the file is ultimately closed, even if an error occurs during execution:
Reset(F); try ... // process file F finally CloseFile(F); end;
The syntax of a try…finally statement is
try statementList1 finally statementList2 end
where each statementList is a sequence of statements delimited by semicolons. The try…finally statement executes the statements in statementList1 (the try clause). If statementList1 finishes without raising exceptions, statementList2 (the finally clause) is executed. If an exception is raised during execution of statementList1, control is transferred to statementList2; once statementList2 finishes executing, the exception is re-raised. If a call to the Exit, Break, or Continue procedure causes control to leave statementList1, statementList2 is automatically executed. Thus the finally clause is always executed, regardless of how the try clause terminates.
If an exception is raised but not handled in the finally clause, that exception is propagated out of the try…finally statement, and any exception already raised in the try clause is lost. The finally clause should therefore handle all locally raised exceptions, so as not to disturb propagation of other exceptions.
Standard Exception Classes and Routines
The SysUtils and System units declare several standard routines for handling exceptions, including ExceptObject, ExceptAddr, and ShowException. SysUtils, System and other units also include dozens of exception classes, all of which (aside from OutlineError) derive from SysUtils.Exception.
The SysUtils.Exception class has properties called Message and HelpContext that can be used to pass an error description and a context ID for context-sensitive online documentation. It also defines various constructor methods that allow you to specify the description and context ID in different ways.
See Also
- Classes and Objects (Delphi)
- Fields (Delphi)
- Methods (Delphi)
- Properties (Delphi)
- Nested Type Declarations
- Class References
- Operator Overloading (Delphi)
- Class and Record Helpers (Delphi)
- Handling Exceptions
|
|
|
Пожалуйста, выделяйте текст программы тегом [сode=pas] … [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как «свернуть» программу в трей.
3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
… (продолжение следует) …
Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.
Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка — 60 дней. Последующие попытки бан.
Мат в разделе — бан на три месяца…
как програмно вызвать except
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
народ что то я не пойму!! как програмно в try .. except .. end вызвать ошибку? |
Сан Иваныч |
|
StrToInt(‘bla-bla-bla’); |
vladimir74 |
|
да нет. try работа if неверные данные !перейти в except с ошибкой! except log(записать ошибку) end; |
Сан Иваныч |
|
if не рулит. Если будет исключение, то программа сама попадет в секцию except. |
Smike |
|
raise Exception.Create; |
New |
|
Цитата vladimir74 @ vladimir74 Ты наверное не понимаешь сам принцип работы try… except… Никаких if не надо! Даже если у тебя в try несколько действий, они будут выполняться по очереди до тех пор, пока не возникнет ошибка. Если ошибка возникнет, программа сама перейдет в except… Тебя это что ли интересует?? А уже обрабатывать ошибку (и записывать ее в лог) — это твои заботы! Чтобы разобраться по какой ошибке приложение перешло в except, можешь в try использовать какой-нибудь флаг, который будет тебе однозначно давать понять, на каком действии произошел сбой. |
vladimir74 |
|
стоп!!!! |
Сан Иваныч |
|
Блин, тебе объясняют, что try…except сам отловит исключение. Без всяких if. Засунь в try мой пример из второго поста и посмотри, что будет |
vladimir74 |
|
Smike Добавлено Цитата Сан Иваныч @ Блин, тебе объясняют, что try…except сам отловит исключение. Без всяких if. Засунь в try мой пример из второго поста и посмотри, что будет блин да я же говорю, что нет в этот момент никаких исключений!! не буду же я на ноль делить, чтоб его вызвать или сравнивать строку с числом!!! глупо это Сообщение отредактировано: vladimir74 — |
Сан Иваныч |
|
vladimir74, то, что ты предлагаешь, можно написать и без исключений. Например, при преобразовании StrToInt проверяешь строку на входе на наличие в ней нечисловых символов, и если таковые будут найдены — пиши, что данные плохие. |
vladimir74 |
|
как бы правильно обьяснить |
Сан Иваныч |
|
А в событии BeforePost() нельзя проверить? И если найдены ошибки, то Abort() и никаких проблем! |
vladimir74 |
|
Сан Иваныч |
Сан Иваныч |
|
Да хоть десять проверок. Именно в BeforePost() пишешь try StrToInt(str); except ShowMessage(‘Неверное значение …..’); WriteToLog(‘ошибка в типе данных для поля XXX’); Abort(); end; // и так для всех проблемных полей |
vladimir74 |
|
Цитата Сан Иваныч @ А в событии BeforePost() нельзя проверить? И если найдены ошибки, то Abort() и никаких проблем!
нет тут процедура связываюшая около 10 таблиц когда insert, когда edit. сильно много проверять придется. |
Демо |
|
procedure TForm1.Button1Click(Sender: TObject); var n,m: Integer; begin n := 18; m := 6; try n := n div m; if n=2 then Raise Exception.Create(‘Error Data2’); if n=3 then Raise Exception.Create(‘Error Data3’); except on E: Exception do ShowMessage(E.Message); end; end; |
vladimir74 |
|
Демо Добавлено Добавлено |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Delphi: Общие вопросы
- Следующая тема
[ Script execution time: 0,0372 ] [ 16 queries used ] [ Generated: 21.09.23, 09:54 GMT ]