│
English (en) │
suomi (fi) │
Free Pascal supports exceptions. Exceptions are useful for error handling and avoiding resource leaks. However, bear in mind that exceptions have a performance impact.
The official documentation is here: Reference guide chapter 17.
By default exceptions are disabled. You can opt in by using the ObjFPC or DELPHI Compiler Mode, or adding -Sx to the compiler commandline. This enables the try
, raise
, except
, and finally
reserved words for use in your own code, but it doesn’t enable exception generation from the RTL. To enable the RTL to raise exceptions instead of generating runtime errors, use the SysUtils unit in your program.
The base Exception
class is found in the SysUtils unit. All exceptions should preferably use this class or its descendants.
SysUtils automatically sets up a basic exception catcher, so any otherwise uncaught exception is displayed in human-readable form, as the program terminates. To generate readable callstacks from caught exceptions in your own code, without necessarily terminating the program, you can use SysUtils functions ExceptAddr
, ExceptFrames
, ExceptFrameCount
, and BackTraceStrFunc
.
Examples
Note that Pascal uses different keywords than some other languages: raise
instead of throw
, and except
instead of catch
. Also, as a compiler design choice, each try
-block can pair with either an except
or finally
block, but not both at the same time. You’ll need to nest try
-blocks if you need except
and finally
protecting the same code.
Error handling
uses sysutils; begin try // Do something that might go wrong. except begin // Try to recover or show the user an error message. end; end; end.
Cleaning up resources
try // Do something that might go wrong. finally // Clean-up code that is always called even if an exception was raised. end;
Exception leaking
Finally-blocks don’t destroy exception objects. Any exception that reaches the program’s «end.» will trigger a memory leak warning. An extra except block can be used to consume such exceptions. This is Delphi-compatible.
begin try // Your main program, where an exception object is created. finally try // Clean-up code. except end; end; end.
Signalling problems
raise Exception.Create('Helpful description of what went wrong');
Using specialised exception types to signal different problems
type EMyLittleException = Class(Exception); begin try raise EMyLittleException.Create('Foo'); except on E : EMyLittleException do writeln(E.Message); on E : Exception do writeln('This is not my exception!'); else writeln('This is not an Exception-descendant at all!'); end; end;
Re-raising exceptions
try // Do something that might go wrong. except // Try to recover or show the user an error message. if recoveredSuccessfully = FALSE then raise; end;
Exception classes
SysUtils defines and raises many specific exception classes.
With SysUtils included in your program, and exceptions enabled, various runtime errors are changed into exceptions. Processor-level interrupts like SIGSEGV or SIGFPU, which normally translate to run-time errors, are also changed to exceptions. For example, run-time error 200 (division by zero) becomes EDivByZero or EZeroDivide, while run-time error 216 (a general protection fault) becomes EAccessViolation.
Best practice
- Raise exceptions to signal that an operation could not be completed, where it normally should have succeeded.
- Do not use exceptions as part of expected control flow. Instead, add checks for common error conditions and return error values the old-fashioned way. For example, input parsing problems or file existence fails are usually not truly exceptional.
- But do raise an exception if it’s critical that the error is noticed; programmers may forget to check for returned error values.
- Naming convention: Prefix exception class names with a capital E.
- Re-raise exceptions in
except
-blocks usingraise;
if you were unable to recover from the problem; this preserves the original exception’s callstack. - Be careful about using the catch-all base
Exception
class, since the underlying code or OS/filesystem might produce an unanticipated exception that slips through your exception handler, potentially corrupting the program state. - When writing a unit or library, if exceptions are used at all, they should be documented clearly up front. This way the unit user knows what to expect.
- Keep exception handling away from code that needs to run as fast as possible.
Performance
Compiler optimisation levels don’t make much difference. All exception blocks use a small amount of wrapper code that can’t be optimised away. There are different ways for a compiler to produce exception code, with varying performance implications. As of FPC 3.0.4, the default exception mechanism uses the standard setjmp/longjmp style. FPC also supports OS-provided Structured Exception Handling; this is the default on Win64, and can be enabled on Win32 (recompile the compiler with $define TEST_WIN32_SEH
). Other mechanisms may be added to FPC in the future.
To get a better feel for what’s going on, try writing a small test program and compiling it with the -a
switch. This leaves behind a human-readable assembly file.
Notes on the setjmp/longjmp method:
Try
-statements insert some extra address calculations, a stack push and pop, some direct jumps, and a conditional jump. This is the setup cost of an exception frame, even if no exception is raised.Except
-statements insert the above, plus a push and pop, more direct jumps, and another conditional jump.Finally
-statements add a pop and a conditional jump.Raise
-statements create a string and pass control to FPC’s exception raiser. The string creation itself can spawn an implicit exception frame for the function/method, due to dynamic string allocations, even if theraise
is not executed. This is why in e.g. container types one often sees theraise
moved to a separate local (private) procedure. This localises the exception frame to that procedure, so the performance hit is only taken if the procedure is called.
Furthermore, generating a human-readable callstack from a raised exception involves time-consuming stack traversal and string manipulation.
With all that said, outside of heavy processing code, the convenience of exceptions usually outweighs their performance impact. Don’t feel bad about using them if it makes your code better.
Further reading
Logging exceptions
Avoiding implicit try finally section
Exceptions vs Error codes — a situation where exceptions might be a danger in some code (see middle of this page)
Блок try…except имеет следущий вид:
Оператор try..except
Если исключение не инициируется во время выполнения списка операторов, то все операторы в списке будут выполняться последовательно до end, за исключением блока except, который будет пропущен,
Если исключение происходит во время выполнения списка операторов, поток выполнения будет прерван и управление будет передано на except блок. Операторы между местом, где произошло исключение и до блока except — игнорируются.
В блоке обработки исключения, проверяется объект исключения, и если есть обработчик исключения, данного типа (класса) или родительского класса объекта исключения, то выполняются операторы после соответствующего do. Используется первый же подходящий объект, данного класса. После того , как блок — do был выполнен, программа продолжается c конца блока (служебное слово end).
Идентификатор (имя, после служебного слова on) в операторе обработки исключений не является обязательным, и если он есть, то он объявляет объект исключения (можно указать имя объекта, а можно только его тип). Он может быть использован для манипуляций с объектом исключения в коде обработки исключений. То, как организует программист блок после слова do, зависит от его потребностей.
Если ни один из обработчиков on не соответствует типу объекта исключения, то выполняется список операторов после else. Если этот список не найден (отсутствует сл. слово else), то обработка исключения передаётся на более высокий уровень (неявный raise). Этот процесс позволяет обрабатывать только некоторые исключения (указанные в блоке try…except).
Если исключение начало обрабатываться, то объект исключения автоматически разрушается вызовом деструктора destroy (самому вызывть ненадо) и упрвление передается инструкции, следующей за блоком try…except. (Если вызов стандартных процедур Exit, Break или Continue выводит управление из обработчика, объект исключения также разрушается).
В качестве примера, рассмотрим предыдущую функцию DoDiv (из предыдущего примера):
Try
Z := DoDiv (X,Y);
Except
On EDivException do Z := 0;
end;
Если Y будет равным нулю, то код функции DoDiv вызовет исключение. Когда это произойдёт, будет создано исключение, и его обработчик установит Z значение 0. Если исключение не происходит, то программа выполнится, игнорируя блок except. Для того, что-бы применить восстановление после ошибки, нужно использовать блок. Блок try…finally, гарантирует, что будут выполнены операторы после сл. слова finally, даже если произойдёт исключение.
Sharpix 6 / 6 / 0 Регистрация: 07.10.2013 Сообщений: 93 |
||||
1 |
||||
19.09.2016, 22:39. Показов 12870. Ответов 1 Метки exception, finally, lazarus, try except, try except finally, try exception, try finally, исключения, лазарус, научите, обработка исключений, отладка, try/catch (Все метки)
Здравствуйте. Научите меня правильно использовать обработку исключений на примере деления на ноль. Добавил просто блок try-except — оно не работает. Не нашел в интернете ни одного рабочего примера.
__________________ 0 |
BOGG ART 587 / 454 / 147 Регистрация: 09.12.2013 Сообщений: 2,385 Записей в блоге: 2 |
||||||||
19.09.2016, 23:54 |
2 |
|||||||
Что в вашем понимании «не работает»? Отладка выскакивает? Ну так и должно быть по умолчанию. В настройках отключите. Или запускайте не из под IDE. Добавлено через 1 минуту
Добавлено через 8 минут
1 |
Глава 6 Программирование приложений с графическим интерфейсом
____________________________________________________________________
но возникновение исключений. После except следуют операторы, которые образуют секцию обработки исключений. Признаком конца секции служит ключевое слово end. Внутри секции программист указывает классы исключе-
ний (говорят еще типы исключений) после слова on, а затем после ключевого слова do оператор обработки исключения, причем оператор может быть со-
ставным. После необязательного else следуют операторы обработки исклю-
чений, не вошедшие в перечень on. Если программисту нужно только устано-
вить сам факт исключения, независимо от типа, то он может просто записать обработчик исключения после слова except.
Вторая конструкция имеет вид:
try
<Потенциально «опасные» операторы, при выполнении которых могут возникнуть исключительные ситуации >
finally
<операторы, которые должны быть выполнены в любом случае, незави-
симо от того, произошло исключение или нет >
end;
В чем их различие? В конструкции try..except если при выполнении операторов секции try возникло исключение, то управление передается в сек-
цию except, где и происходит обработка исключения. Если же исключения не произошло, то операторы блока except просто пропускаются. В конструкции try..finally операторы будут выполнены независимо от того, произошло исключение или нет.
Рассмотрим пример:
try
num:=StrToInt(Stroka);
521
6.3 Визуальное программирование в среде Lazarus
____________________________________________________________________
except
on EConvertError do
ShowMessage(‘Ошибка преобразования строки в целое число‘);
end;
Здесь сообщение будет выведено только в том случае, когда невозможно преобразование строки символов в целое число, то есть когда возникнет ис-
ключение EConvertError.
Конструкции try..except и try..finally могут быть вложены друг в друга на неограниченную глубину. Рассмотрим реализацию примера с файлом.
Procedure Test; var
F: TextFile; number: integer; s: string;
begin
AssignFile(F, ‘Data.txt’);
Rewrite(F);
s:= ’12#4′; |
// В файл намеренно записывается |
|
Writeln(F, s); |
// ошибочная строка символов |
|
Reset(F); |
||
try |
// начало секции (блока) try..except |
|
Readln(F, s); |
||
try |
// начало секции try..finally |
number:= StrToInt(s);
finally
CloseFile(F); // эти два оператора будут выполнены
522
Глава 6 Программирование приложений с графическим интерфейсом
____________________________________________________________________
DeleteFile(‘Data.txt’); // в любом случае
end; // конец секции try..finally
except
on EConvertError do
ShowMessage(‘Ошибка преобразования’); end; // конец секции try..except
end;
Вернемся к примеру, в котором осуществляется ввод двух целых чисел и выполняются четыре арифметических действия (сложение, вычитание, умно-
жение и деление нацело), рис. 6.30. Модифицируем программу, добавив в него обработку исключений. Перепишите обработчики события OnKeyPress для
LabeledEdit1 и событий OnClick кнопок SpeedButton1, SpeedButton2, SpeedButton3 и SpeedButton4 в виде:
procedure TForm1.LabeledEdit1KeyPress(Sender: TObject; var Key: char);
begin
if Key = #13 then begin
try StrToInt(LabeledEdit1.Text);
except
on EConvertError do begin
ShowMessage(‘Ошибка преобразования! Вероятно, ‘ +
‘Вы ошиблись при вводе числа‘);
exit;
end;
523
6.3 Визуальное программирование в среде Lazarus
____________________________________________________________________
end;
LabeledEdit2.SetFocus; SpeedButton1.Down:= false; SpeedButton2.Down:= false; SpeedButton3.Down:= false; SpeedButton4.Down:= false; exit;
end;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject); begin
try LabeledEdit3.Text:=IntToStr(StrToInt(LabeledEdit1.Text)
+ StrToInt(LabeledEdit2.Text));
except
on EConvertError do begin
ShowMessage(‘Ошибка преобразования! Вероятно, ‘ + ‘Вы ошиблись при вводе второго числа‘);
exit;
end;
end;
StatusBar1.SimpleText:= ‘Сложение‘;
LabeledEdit1.SetFocus;
LabeledEdit1.SelectAll;
end;
procedure TForm1.SpeedButton2Click(Sender: TObject);
524
Глава 6 Программирование приложений с графическим интерфейсом
____________________________________________________________________
begin try
LabeledEdit3.Text:=IntToStr(StrToInt(LabeledEdit1.Text)
— StrToInt(LabeledEdit2.Text));
except
on EConvertError do begin
ShowMessage(‘Ошибка преобразования! Вероятно, ‘ + ‘Вы ошиблись при вводе второго числа‘);
exit;
end;
end;
StatusBar1.SimpleText:= ‘Вычитание‘;
LabeledEdit1.SetFocus;
LabeledEdit1.SelectAll;
end;
procedure TForm1.SpeedButton3Click(Sender: TObject); begin
try LabeledEdit3.Text:=IntToStr(StrToInt(LabeledEdit1.Text)
* StrToInt(LabeledEdit2.Text));
except
on EConvertError do begin
ShowMessage(‘Ошибка преобразования! Вероятно, ‘ +
‘Вы ошиблись при вводе второго числа‘);
exit;
end;
525
6.3 Визуальное программирование в среде Lazarus
____________________________________________________________________
end;
StatusBar1.SimpleText:= ‘Умножение‘;
LabeledEdit1.SetFocus;
LabeledEdit1.SelectAll;
end;
procedure TForm1.SpeedButton4Click(Sender: TObject); begin
try LabeledEdit3.Text:=IntToStr(StrToInt(LabeledEdit1.Text)
div StrToInt(LabeledEdit2.Text));
except
on EConvertError do begin
ShowMessage(‘Ошибка преобразования! Вероятно, ‘ + ‘Вы ошиблись при вводе второго числа‘);
exit;
end;
on EDivByZero do begin
ShowMessage(‘Ошибка! Произошло деление на ноль. Вероятно, ‘ + ‘Вы ошиблись при вводе второго числа‘);
exit;
end;
end;
StatusBar1.SimpleText:= ‘Деление нацело‘;
LabeledEdit1.SetFocus;
LabeledEdit1.SelectAll;
end;
526
Глава 6 Программирование приложений с графическим интерфейсом
____________________________________________________________________
Теперь при вводе недопустимого символа для целого числа, а также при вводе числа 0 в качестве второго операнда программа перехватывает исключе-
ния и реагирует соответствующим образом.
Имейте в виду, если вы запускаете программу из среды Lazarus, то исклю-
чения будет перехватывать отладчик. Поэтому лучше запускать программу из командной строки или в настройках окружения для отладчика добавьте нужные вам типы исключений в список игнорирования.
Обработку исключений вполне можно применять и в консольных прило-
жениях. Вспомним программу из 2.1.14. Организуем контроль ввода данных,
используя механизм исключений.
program int_operations_control; {$mode objfpc}{$H+}
uses
CRT, FileUtil, SysUtils; var
A, B, C: integer; begin
writeln(UTF8ToConsole(‘Введите два числа‘)); readln(A, B);
writeln(‘A= ‘, A, ‘ B= ‘, B); C:= A + B;
writeln(UTF8ToConsole(‘Демонстрация сложения, C= ‘), C); C:= A * B;
writeln(UTF8ToConsole(‘Демонстрация умножения, C= ‘), C); try
C:= A div B;
writeln(UTF8ToConsole(‘Демонстрация деления нацело, C= ‘),
C);
527
6.3 Визуальное программирование в среде Lazarus
____________________________________________________________________
except
on EDivByZero do begin
writeln(UTF8ToConsole(‘Ошибка!! Деление на ноль.‘)); writeln(UTF8ToConsole(‘Нажмите любую клавишу‘)); readkey;
exit;
end;
end;
C:= A mod B;
writeln(UTF8ToConsole(‘Остаток от деления, C= ‘), C); C:= A — B;
writeln(UTF8ToConsole(‘Демонстрация вычитания, C= ‘), C); writeln(UTF8ToConsole(‘Нажмите любую клавишу‘)); readkey;
end.
Сравните эту программу с программой из 2.1.25.
Контроль и обработку ошибок с применением механизма исключений удобнее использовать, когда работа программы уже не зависит от действий пользователя, т.е. все необходимые данные от пользователя уже получены,
программа перешла непосредственно к обработке данных – идут вычисления,
происходит чтение и запись в файлы, обращения к различным внешним уст-
ройствам и т.д. На этапе же ввода данных, когда пользователь в режиме диало-
га вводит какие-то данные с клавиатуры, удобнее использовать другой способ контроля. Поскольку обработка исключений происходит после завершения ввода пользователем, то есть этим происходит только констатация факта, что ошибка пользователем уже совершена. Приходится организовывать повторный ввод, причем с того места, где пользователь ошибся. А это приведет к усложне-
528
Глава 6 Программирование приложений с графическим интерфейсом
____________________________________________________________________
нию кода. Гораздо разумнее и эффективней при вводе данных просто не давать пользователю совершить ошибку. Например, если пользователь должен вво-
дить только целые числа, проще контролировать введенные пользователем символы и, если будет попытка ввести недопустимый для целых чисел символ,
просто этот символ игнорировать.
Для этого удобнее использовать другую разновидность компонента TEdit
– TMaskEdit из страницы Additional.
6.3.7.1. Компонент TMaskEdit
В этом компоненте имеется свойство EditMask, с помощью которого можно задать маску ввода. Маска это некий шаблон, задающий какие символы может вводить пользователь в окне ввода. Недопустимые символы игнориру-
ются. Маска состоит из трех частей, между которыми ставится точка с запятой
(;). В первой части маски – шаблоне записываются символы (табл. 6.2), кото-
рые указывают какие символы можно вводить в каждой позиции.
Таблица 6.2
Означает, что в EditText недостающие символы предваряются
!пробелами. В случае отсутствия символа пробелы размещаются в конце.
0 Означает, что в данной позиции должна быть цифра.
9 Означает, что в данной позиции может быть цифра или ничего.
#Означает, что в данной позиции может быть цифра, знак « + », знак «-» или ничего.
Далее через точку с запятой (;) записывается 1 или 0 в зависимости от того,
надо или нет, чтобы символы, добавляемые маской, включались в свойство
Text компонента. В третьей части маски указывается символ-заполнитель, ис-
пользуемый для обозначения позиций, в которых еще не осуществлен ввод. Ус-
тановить нужную маску можно прямо в свойстве EditMask, введя необходи-
мые символы маски или в редакторе масок, открыть который можно нажав
529
6.3 Визуальное программирование в среде Lazarus
____________________________________________________________________
кнопку с троеточием, рис. 6.33.
Рис. 6.33. Окно редактора масок
Прочитать результат ввода можно или в свойстве Text, которое, в зависи-
мости от вида второй части маски, включает или не включает в себя символы маски, или в свойстве EditText, содержащем введенный текст вместе с сим-
волами маски.
Итак, давайте применим для нашего примера компонент TMaskEdit.
Удалите из формы LabeledEdit1 и LabeledEdit2. Перенесите на их ме-
сто два компонента TMaskEdit. Также вставьте в форму два компонента
TLabel. В общем, восстановите внешний вид формы как на рисунке 6.30.
Установите следующие свойства MaskEdit1:
AutoSelect = true
EditMask = #9999;0;
TabOrder = 0
свойство Text оставьте пустым.
В качестве символа заполнителя в окошке «Символы для пробелов» в ре-
дакторе масок введите пробел вместо знака подчеркивания.
Установите свойства MaskEdit2:
530
В процессе выполнения скрипта возможно возникновение исключительных ситуаций, ошибок. Если это происходит, то пользователь видит сообщение об ошибке «Опа!…». Вы можете перехватить ошибку и обработать ее особым образом. Для обработки исключительных ситуаций блок кода, в котором они возможны, заключается в составной оператор try.
try FS := TFileStream.Create(MyFileName, fmCreate); except MsgBox(‘Ошибка’, ExceptionToString(ExceptionType, ExceptionParam); end;
Если в блоке try происходит исключение, то выполнение кода прерывается и выполняется код в блоке except.
Есть другая конструкция – try..finally:
try FS := TFileStream.Create(MyFileName, fmCreate); try … finally FS.Free; end; except MsgBox(‘Ошибка’, ExceptionToString(ExceptionType, ExceptionParam); end;
Код в блоке finally выполняется в любом случае, нормально ли отработал код или произошла ошибка. Если в блоке try используется ключевое слово exit (выход из процедуры/функции), то код в блоке finally также выполняется.
В операторе try могут быть использованы оба блока:
try … except … finally … end;
Вы можете программно сгенерировать исключение, используя процедуру RaiseException:
var filename, emsg: string; begin filename = ''; try if filename = '' then RaiseException(erCustomError, 'Имя файла не может быть пустым'); … except emsg := ExceptionToString(ExceptionType, ExceptionParam); … end; end;
При обработке исключительных ситуаций могут быть использованы следующие процедуры и функции:
function ExceptionToString(Err: TIFException; Param: String): String
Формирует по типу исключения строку сообщения об ошибке.
function ExceptionType: TIFException
Возвращает тип возникшего исключения.
function ExceptionParam: String
Возвращает текст сообщения об ошибке.
procedure RaiseException(Err: TIFException; Param: String)
Генерирует исключительную ситуацию.
procedure RaiseLastException
Генерирует последнюю возникшую исключительную ситуацию.
Как узнать код ошибки try except
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
Senior Member Рейтинг (т): 3 |
Подскажите, можно ли как нибудь отловить код ошибки?
//readln(A); =0; try Z:=Z/A; except on E: exception do ShowMessage(e.messages); end; Ну это конечно ясно, что он напишет ‘Division by zero’, но это во-первых по английски, а во вторых, если мы напишем сразу сообщение по русски, т.е. так:
//readln(A); =0; try Z:=Z/A; except ShowMessage(‘Деление на ноль!’); end;
и допустим, что ошибка произошла не из-за того, что делится на ноль, а из-за чего-нибудь другова — то естественно сообщение будет ложным, а точнее неверным.
//readln(A); =0; try Z:=Z/A; except on E: exception do if (e.messages=’Division by zero’) then ShowMessage(‘Деление на ноль’) else ShowMessage(e.messages); end;
Почему вариант тупой, да потому что, перед тем как вставить текст сообщения об ошибке надо: Хочется что-то типа того, что текст ошибки сообщения мог бы заменять какой-нибудь её индетификатор, чтобы можно было типа такого:
//readln(A); =0; try Z:=Z/A; except on E: exception do if (e.код_ошибки=123) then ShowMessage(‘Деление на ноль’) else ShowMessage(e.messages); end; где «123» есть индетификатор ошибки, т.е. заменяет сам текст ‘Division by zero’. Да, и у e нет параметра на английском, что я написал на русском код_ошибки. Ууууу…. Пока писал — сам устал. |
CodeMonkey |
|
on E: EDivByZero do Подробно и в деталях. |
Continental |
|
Senior Member Рейтинг (т): 3 |
Спасибо за ссылку.
except on E: EIBInterBaseError do begin sShowMessage(e.ClassName,e.Message); exit; end; а он мне пишет: Неизвестная переменная EIBInterBaseError Вообще класс такой есть, он даже его показывает, если я делаю так:
try Connected:=true; except on E: exception do sShowMessage(e.ClassName,e.Message); end; Вот e.ClassName как раз и есть EIBInterBaseError, только я не могу класс коде указать. Сообщение отредактировано: Continental — 27.10.09, 14:48 |
mitrich |
|
Senior Member Рейтинг (т): 99 |
Хелп говорит, что EIBInterBaseError находится в юните IB |
CodeMonkey |
|
Цитата Continental @ 27.10.09, 14:21 А вот в каком модуле находятся классы исключений при работе с ibx компонентами.
Пуск/Поиск. *.pas с текстом EIBInterBaseError. Цитата Continental @ 27.10.09, 14:21 Вообще класс такой есть, он даже его показывает, если я делаю так Вместо
except on E: SomeClass do … можно (но не рекомендуется) использовать:
except on E: Exception do if E.ClassName = ‘SomeClass’ then … else raise; |
Continental |
|
Senior Member Рейтинг (т): 3 |
ММММ…. всё равно прочитав всё, не могу реализовать то что я хочу. Так вот я хочу сказать, что в двух случаях ошибки совсем разные, а вот класс исключения один и тот же.
try IBDataBase.Connected:=true; except on E : что здесь должно быть, чтобы определить 1-й случай? do begin sShowMessage(‘несуществующий путь бд!’); exit; end; on E : что здесь должно быть, чтобы определить 2-й случай? do begin sShowMessage(‘неверный пользователь или пароль’); exit; end; on E : Exception do begin //люая другая ошибка sShowMessage(e.ClassName,E.Message+’ совершенно другая ошибка’); exit; end; end; Помогите разобраться пожалуйста, я очень хочу сделать так, но не знаю. |
CodeMonkey |
|
Цитата Continental @ 27.10.09, 15:51 Так вот я хочу сказать, что в двух случаях ошибки совсем разные, а вот класс исключения один и тот же. Если у вашего EIBDataBaseError нет какого-нибудь IBErrorCode — то это криво спроектированные классы Interbase. Цитата Continental @ 27.10.09, 15:51 Так я не могу понять, как же мне отловить первую ошибку, или вторую вот с помощью такой конструкции: Если никакого поля с кодом нет, то по-хорошему — никак. (Т.е. вы можете, конечно, попробовать анализировать сообщение, но ведь его могут локализовать). Добавлено 27.10.09, 16:13
try IBDataBase.Connected:=true; except on E: EIBDataBaseError do if E.ErrorCode = XXX then begin sShowMessage(‘несуществующий путь бд!’); exit; end else if E.ErrorCode = YYY then begin sShowMessage(‘неверный пользователь или пароль’); exit; end else raise; end; Добавлено 27.10.09, 16:15
try IBDataBase.Connected:=true; except on E: Exception do begin if E is EIBDataBaseError then begin if EIBDataBaseError(E).ErrorCode = XXX then begin sShowMessage(‘несуществующий путь бд!’); exit; end else if EIBDataBaseError(E).ErrorCode = YYY then begin sShowMessage(‘неверный пользователь или пароль’); exit; end; end; ShowMessage(e.ClassName,E.Message+’ совершенно другая ошибка’); end; end; Добавлено 27.10.09, 16:17 |
mitrich |
|
Senior Member Рейтинг (т): 99 |
Цитата CodeMonkey @ 27.10.09, 16:11 Если у вашего EIBDataBaseError нет какого-нибудь IBErrorCode
Именно так это свойство и называется, что нетрудно узнать из хелпа (который надеется на длительное и плодотворное сотрудничество с Continental ) |
Continental |
|
Senior Member Рейтинг (т): 3 |
С облегчением …
try Connected:=true; except on E : EIBInterBaseError do begin sShowMessage(IntToStr(e.IBErrorCode),E.Message); exit; end; Спасибо всем, что открыли глаза. Добавлено 27.10.09, 17:01
except on E: Exception do begin if E is EIBDataBaseError then begin if EIBDataBaseError(E).ErrorCode = XXX then Я теперь это заделал в applicationevents. Теперь у меня будет и лог вестись и сообщения об ошибках выводится! Е-Е-Е! |
CodeMonkey |
|
Для лога ошибок имеет смысл использовать хук на исключения. См. JCL, EurekaLog, madExcept. Помимо сообщения этот способ позволит получить стек вызовов и состояние процессора в момент ошибки. С этой информацией зачастую продиагностировать ошибку можно даже не запуская отладчик. |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Delphi: Общие вопросы
- Следующая тема
[ Script execution time: 0,0466 ] [ 16 queries used ] [ Generated: 28.01.23, 21:30 GMT ]
Sharpix 6 / 6 / 0 Регистрация: 07.10.2013 Сообщений: 93 |
||||
1 |
||||
19.09.2016, 22:39. Показов 14412. Ответов 1 Метки exception, finally, lazarus, try except, try except finally, try exception, try finally, исключения, лазарус, научите, обработка исключений, отладка, try/catch (Все метки)
Здравствуйте. Научите меня правильно использовать обработку исключений на примере деления на ноль. Добавил просто блок try-except — оно не работает. Не нашел в интернете ни одного рабочего примера.
0 |
BOGG ART 589 / 456 / 147 Регистрация: 09.12.2013 Сообщений: 2,385 Записей в блоге: 2 |
||||||||
19.09.2016, 23:54 |
2 |
|||||||
Что в вашем понимании «не работает»? Отладка выскакивает? Ну так и должно быть по умолчанию. В настройках отключите. Или запускайте не из под IDE. Добавлено через 1 минуту
Добавлено через 8 минут
1 |
Блок try…except имеет следущий вид:
Оператор try..except
Если исключение не инициируется во время выполнения списка операторов, то все операторы в списке будут выполняться последовательно до end, за исключением блока except, который будет пропущен,
Если исключение происходит во время выполнения списка операторов, поток выполнения будет прерван и управление будет передано на except блок. Операторы между местом, где произошло исключение и до блока except — игнорируются.
В блоке обработки исключения, проверяется объект исключения, и если есть обработчик исключения, данного типа (класса) или родительского класса объекта исключения, то выполняются операторы после соответствующего do. Используется первый же подходящий объект, данного класса. После того , как блок — do был выполнен, программа продолжается c конца блока (служебное слово end).
Идентификатор (имя, после служебного слова on) в операторе обработки исключений не является обязательным, и если он есть, то он объявляет объект исключения (можно указать имя объекта, а можно только его тип). Он может быть использован для манипуляций с объектом исключения в коде обработки исключений. То, как организует программист блок после слова do, зависит от его потребностей.
Если ни один из обработчиков on не соответствует типу объекта исключения, то выполняется список операторов после else. Если этот список не найден (отсутствует сл. слово else), то обработка исключения передаётся на более высокий уровень (неявный raise). Этот процесс позволяет обрабатывать только некоторые исключения (указанные в блоке try…except).
Если исключение начало обрабатываться, то объект исключения автоматически разрушается вызовом деструктора destroy (самому вызывть ненадо) и упрвление передается инструкции, следующей за блоком try…except. (Если вызов стандартных процедур Exit, Break или Continue выводит управление из обработчика, объект исключения также разрушается).
В качестве примера, рассмотрим предыдущую функцию DoDiv (из предыдущего примера):
Try
Z := DoDiv (X,Y);
Except
On EDivException do Z := 0;
end;
Если Y будет равным нулю, то код функции DoDiv вызовет исключение. Когда это произойдёт, будет создано исключение, и его обработчик установит Z значение 0. Если исключение не происходит, то программа выполнится, игнорируя блок except. Для того, что-бы применить восстановление после ошибки, нужно использовать блок. Блок try…finally, гарантирует, что будут выполнены операторы после сл. слова finally, даже если произойдёт исключение.
Об исключениях
Исключительная ситуация возникает при ошибке или прерывании нормального хода выполнения программы каким-либо событием. Исключение передает контроль выполнения программы обработчику исключительной ситуации, который позволяет отделить нормальную логику работы программы от обработки ошибок. Поскольку исключения являются объектами, они могут быть сгруппированы в иерархию, использующую наследование, а новые исключения могут объявляться без изменения уже готового кода. Исключение может передавать информацию (например, сообшение об ошибке) из точки возникновения исключительной ситуации к месту ее обработки.
Когда приложение подключает модуль SysUtils большая часть ошибок, возникающих при выполнении программы, автоматически преобразуется в исключения. Значительная часть ошибок, результатом которых могло бы стать завершение программы (такие как недостаток памяти, деление на ноль, ошибки общей защиты) перехватываются и обрабатываются.
Когда следует применять исключения
Исключения обеспечивают удобный способ отслеживать ошибки при выполнении программы без применения массивных условных конструкций. Требования, накладываемые семантикой обработки исключительных ситуаций, влекут за собой увеличение объема кода и данных, а также снижение производительности приложения. По этому, не смотря на то, что существует возможность создавать исключения практически в любой ситуации, защищая любой блок исходного кода заключением его в структуру try…except или try…finally, на практике эти инструменты лучше применять только в особых случаях.
Обработка исключительных ситуаций подходит для:
- трудноопределимых ошибок, вероятность возникновения которых невелика, но последствия которых могут оказаться серьезными (например, аварийное завершение приложения);
- ошибок, возникновение которых трудно отследить при помощи инструкций if…then ;
- случаев, когда необходимо реагировать на исключительные ситуации, создаваемые операционной системой или другими программами, код которых вы не контролируете.
Обычно исключения используются для обработки ошибок аппаратного обеспечения, памяти, обработки ввода/вывода и ошибок операционной системы.
Инструкции условных переходов зачастую являются лучшим способом обработки ошибок. Например, если перед тем как открыть файл, вы хотите убедиться, что файл существует, можно сделать это следующим образом:
try AssignFile(F, FileName); Reset(F); // если файл не найден, //возникает исключительная ситуация EInOutError except on Exception do ... end;
Но для экономии ресурсов можно использовать и другой способ:
if FileExists(FileName) then // возвращает False если файл не найден //исключительной ситуации не возникает begin AssignFile(F, FileName); Reset(F); end;
Утверждения (Assertions) дают еще одну возможность проверки логических условий в вашем исходном коде. Если при выполнении инструкции Assert происходит ошибка, приложение либо завершается, либо (в том случае, если подключен модуль SysUtils) создает исключение SysUtils.EAssertionFailed. Утверждения должны использоваться только для проверки условий, невыполнения которых вы не ожидаете.
Объявление типов исключений
Исключения объявляются как и прочие классы. На самом деле в качестве исключения вы можете использовать экземпляр любого класса, но рекомендуется наследовать исключения от класса SysUtils.Exception, который объявлен в модуле SysUtils.
Вы можете объединять исключения в семейства при помощи наследования. Приведенные ниже объявления из модуля SysUtils объявляют семейство исключений для обработки математических ошибок:
type EMathError = class(Exception); EInvalidOp = class(EMathError); EZeroDivide = class(EMathError); EOverflow = class(EMathError); EUnderflow = class(EMathError);
Сделав такие объявления, вы можете создать один обработчик исключений для SysUtils.EMathError, который сможет работать с SysUtils.EInvalidOp, SysUtils.EZeroDivide, SysUtils.Overflow и SysUtils.EUnderflow.
Классы исключений иногда содержат поля, методы или свойства для передачи дополнительной информации об ошибке:
type EInOutError = class(Exception) ErrorCode: Integer; end;
Инициирование и обработка исключений
Для инициирования исключения вам необходимо использовать экземпляр класса исключения с инструкцией raise:
raise EMathError.Create;
Вообще говоря, инструкция инициирования исключения имеет следующий вид:
raise object at address
где object и at address являются опциональными. Когда указывается address, им может быть любое выражение, которое можно обработать как указатель. Обычно это указатель на процедуру или функцию. Например:
raise Exception.Create('Missing parameter') at @MyFunction;
Этой опцией можно инициировать исключение из участка стека, предшествующего тому, в котором на самом деле случилась ошибка. Когда исключение инициировано, то есть после вызова инструкции raise, управление им осуществляется по специальному принципу обработки исключительных ситуаций. Инструкция raise не возвращает управление в точку своего выполнения. Вместо этого управление передается обработчику исключений соответствующего класса, который находится в самом большом уровне вложенности блоков try except (т.е. относящийся к блоку, вход в который был осуществлен позже всего).
Например, приведенная ниже функция преобразует строку в целое число и инициирует исключение в том случае, если результирующее значение не находится в обозначенном диапазоне.
function StrToIntRange(const S: string; Min, Max: Longint): Longint; begin Result := StrToInt(S); // StrToInt объявлен в 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;
Обратите внимание на метод CreateFmt , вызываемый в инструкции raise. Класс SysUtils.Exception и его потомки оснащены особыми конструкторами, которые предоставляют возможность создавать сообщения об ошибках и идентификаторы контекста.
Инициированное исключение автоматически разрушается после его обработки. Не следует пытаться разрушать исключения самостоятельно.
Замечание: Вызов (инициирование) исключения в секции инициализации модуля может не привести к ожидаемому результату. Обычно обработка исключительных ситуаций выполняется из модуля SysUtils, который должен быть инициализирован прежде чем выполнять такие функции. Если исключительная ситуация имеет место в процессе инициализации – все инициализированные модули (включая SysUtils) финализируются, а исключение инициируется повторно. Затем, после перехвата, исключение обрабатывается (обычно завершением приложения). Инициирование исключений в секции финализации модуля также может не привести к нужному результату, в том случае, если на момент инициирования исключения модуль SysUtils уже финализирован.
Инструкции Try…except
Исключительные ситуации обрабатываются обрабатываются внутри конструкций try…except. Например:
try X := Y/Z; except on EZeroDivide do HandleZeroDivide; end;
Эта конструкция предпринимает попытку деления y на z, а в случае возникновения исключительной ситуации SysUtils.EZeroDivide вызывает подпрограмму HandleZeroDivide.
Синтаксис инструкции try…except:
try statements except exceptionBlock end
где statements – это последовательность инструкций, разделенных точками с запятой (;), а exceptionBlock – может быть:
- еще одной последовательностью инструкций
- еще одной последовательностью инструкций или последовательностью обработчиков исключений, разделяемых инструкциями else.
Обработчик исключения имеет вид:
on identifier: type do statement
где identifier: необязателен (если присутствует – может быть любым допустимым идентификатором), type – это тип для представления исключений, а statement – это любая инструкция.
Инструкция try…except выполняет команды из изначального списка. Если исключительных ситуаций не возникло, блок исключений игнорируется и управление передается следующей части программы.
Если в процессе выполнения списка команд возникла исключительная ситуация (она может быть инициирована инструкцией raise в списке команд или внутри процедуры или функции, вызываемой из этого списка) предпринимается попытка обработки исключительной ситуации:
Если какой-либо из обработчиков в exception block подходит для обработки инициированного исключения, управление передается первому такому обработчику. Обработчик подходит для исключения только в том случае, когда тип, указанный в нем, является типом исключения или предком его класса.
Если обработчика не найдено, управление передается инструкции в секции else (если таковая присутствует).
Если блок обработчиков – это просто последовательность инструкций без каких либо обработчиков – управление передается первой инструкции в этой последовательности.
Если ни одно из указанных выше условий не выполнено, поиск продолжается в блоке обработчиков внешней инструкции try…except , выход из которой еще не выполнен. Если и там не находится соответствующего обработчика, секции else или последовательности инструкций, поиск продолжается в следующей внешней инструкции и так далее. Если в самой внешней инструкции исключительная ситуация не будет обработана – приложение завершается.
Когда исключительная ситуация обработана, в стеке находится процедура или функция, содержащая инструкцию try…except , в которой произошла обработка и управление передается выполненному обработчику, секции else или последовательности инструкций. Этот процесс отменяет все вызовы процедур и функций, имевшие место после входа в блок try…except, в котором исключительная ситуация была обработана. Объект исключения автоматически разрушается вызовом деструктора Destroy и упрвление передается инструкции, следующей за блоком try…except. (Если вызов стандартных процедур Exit, Break или Continue выводит управление из обработчика, объект исключения также разрушается).
В следующем примере первый обработчик обрабатывает исключение деления на ноль, второй – ошибки переполнения, а последний – остальные математические исключения. SysUtils.EMathError указан последним в блоке обработчиков, так как он является предком обоих этих классов. Если бы он указан первым, последующие обработчики никогда не были бы вызваны:
try ... except on EZeroDivide do HandleZeroDivide; on EOverflow do HandleOverflow; on EMathError do HandleMathError; end;
В обработчике исключения перед именем класса исключения можно указать идентификатор. Таким образом объявляется идентификатор для представления объекта исключения в процессе выполнения инструкции, следующей за on…do. Видимость идентификатора ограничивается этой инструкцией. Например:
try ... except on E: Exception do ErrorDialog(E.Message, E.HelpContext); end;
Если блок исключения имеет секцию else, в этой секции происходит обработка всех исключений, которые не были обработаны в блоке обработчика исключения. То есть:
try ... except on EZeroDivide do HandleZeroDivide; on EOverflow do HandleOverflow; on EMathError do HandleMathError; else HandleAllOthers; end;
В этом примере секция else обрабатывает все исключения, не являющимися SysUtils.EMathError.
Блок исключения, не содержащий обработчиков, но содержащий список инструкций, обрабатывает любые исключения. Например:
try ... except HandleException; end;
Здесь подпрограмма HandleException обрабатывает все исключенительные ситуации, которые возникают при выполнении инструкций между try и except.
Повторное инициирование исключений
Когда ключевое слово raise включается в блок исключения без указания ссылки на объект, оно инициирует исключение, уже обработанное в блоке. Это позволяет обработчику исключения среагировать на ошибку и затем повторно инициировать исключение. Повторное инициирование исключения может оказаться полезным, когда процедура или функция должна освободить память после своего выполнения, но не может полностью обработать исключение.
Например, функция GetFileList создает объект TStringList и заполняет его именами файлов, расположенными в определенной для поиска папке:
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 создает объект TStringList, затем применяет функции FindFirst и FindNext (определены в модуле SysUtils) для его инициализации. Если в процессе инициализации происходит ошибка – например, из-за того, что путь задан неверно или для заполенения списка строк недостаточно памяти — GetFileList нужно высвободить память созданного списка при том, что вызывающая подпрограмма ничего не знает о его существовании. По этой причине, инициализация списка строк выполняется внутри инструкции try…except. Если возникает исключительная ситуация, блок обработки исключения разрушает список строк и повторно инициирует исключение.
Встроенные исключения
Код, выполняемый в обработчике исключения может сам инициировать и обрабатывать исключительные ситуации. Поскольку исключения обрабатываются внутри обработчика, они не оказывают влияния на первичное исключение. Тем не менее, если исключение инициированное внутри обработчика, не обрабатывается в нем, первичное исключение теряется. Эту ситуацию иллюстрирует функция Tan, код которой приведен ниже:
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;
Если исключение SysUtils.EMathError инициируется при выполнении Tan, обработчик инициирует исключение ETrigError. Поскольку Tan не имеет обработчика для ETrigError, это исключение выходит из обработчика, тем самым вызывая разрушение исключения SysUtils.EMathError. Вызывающей подпрограмме передается ETrigErrorexception, инициированное внутри функции Tan.
Инструкции Try…finally
Иногда вам необходимо быть уверенными, что некоторые части операции выполнены, вне зависимости от того, прерывается ли выполнение операции исключением или нет. Например, если подпрограмма получает управление ресурсом, важно чтобы память, которую он занимает, была освобождена несмотря на то, что подпрограмма завершается ошибкой. В таких ситуациях вы можете использовать инструкцию try…finally.
В следующем примере показан код, который открывает и обрабатывает файл, при этом файл будет в конечном итоге закрыт, даже если в процессе обработки произойдет ошибка:
Reset(F); try ... // process file F finally CloseFile(F); end;
Синтаксис инструкции try…finally выглядит следующим образом:
try statementList1 finally statementList2 end
где каждый statementList – это последовательность инструкций, разделенных точками с запятой (;). Инструкция try…finally выполняет инструкции в statementList1 (секция try). Если выполнение statementList1 завершается без ошибок, выполняется statementList2 (секция finally). Если в процессе выполнения statementList1 возникает исключительная ситуация, управление передается в statementList2, когда statementList2 завершает свою работу исключение инициируется повторно. Если в процессе выполнения statementList1 происходит вызов процедур Exit, Break или Continue – управление выходит из statementList1 , statementList2 выполняется автоматически. Таким образом секция finally выполняется всегда, вне зависимости от того, как завершается работа секции try.
Если в секции finally возникает исключительная ситуация, которая не обрабатывается, исключение выводится из из инструкции try…finally, а исключение, инициированное в секции try, разрушается. Таким образом, секция finally должна обработать все внутренние исключительные ситуации с тем, чтобы они не были переданы за пределы секции.
Стандартные классы подпрограммы для работы с исключениями
Модули SysUtils и System объявляют несколько стандартных подпрограмм для обработки исключений, включая ExceptObject, ExceptAddr и ShowException. SysUtils, System и прочие модули включают в себя множество классов исключений, которые наследуются от SysUtils.Exception (или от OutlineError).
Класс SysUtils.Exception имеет свойства Message и HelpContext, которые можно использовать для передачи описания ошибки и идентификатора контекста для контекстной онлайн документации. Кроме того, этот класс объявляет конструкторы, позволяющие различными способами задавать описание и идентификатор контекста.