Lazarus try except текст ошибки








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 using raise; 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 the raise is not executed. This is why in e.g. container types one often sees the raise 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)

Sharpix

6 / 6 / 0

Регистрация: 07.10.2013

Сообщений: 93

1

19.09.2016, 22:39. Показов 14080. Ответов 1

Метки exception, finally, lazarus, try except, try except finally, try exception, try finally, исключения, лазарус, научите, обработка исключений, отладка, try/catch (Все метки)


Студворк — интернет-сервис помощи студентам

Здравствуйте. Научите меня правильно использовать обработку исключений на примере деления на ноль. Добавил просто блок try-except — оно не работает. Не нашел в интернете ни одного рабочего примера.

Pascal
1
2
3
4
5
6
7
8
procedure TMainForm.ButtonDivideClick(Sender: TObject);
begin
try
  EditResult.Text := FloatToStr(StrToFloat(EditDelimoe.Text) / StrToFloat(EditDelitel.Text));
  except
    on EZeroDivide do StatusBar.SimpleText := 'На ноль делить нельзя';
end;
end;



0



BOGG ART

588 / 455 / 147

Регистрация: 09.12.2013

Сообщений: 2,385

Записей в блоге: 2

19.09.2016, 23:54

2

Что в вашем понимании «не работает»? Отладка выскакивает? Ну так и должно быть по умолчанию. В настройках отключите. Или запускайте не из под IDE.

Добавлено через 1 минуту

Pascal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Procedure TForm1.Button1Click(Sender: TObject);
Var A, B, C: Double;
Begin
Try
      A:=StrToFloat(Edit1.Text);
      B:=StrToFloat(Edit2.Text);
      C:=A/B;
      Edit3.Text:=FloatToStr(C);
Except
      On E: EZeroDivide Do
            ShowMessage('EZeroDivide --- '+E.ToString);
      On E: Exception Do
            ShowMessage('Exception --- '+E.ToString);
      End;
End;

Добавлено через 8 минут

Pascal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Procedure TForm1.Button2Click(Sender: TObject);
Var SomeClassA: TBitmap; SomeClassB: TJPEGImage; SomeClassC: TFontDialog;
Begin
SomeClassA:=TBitmap.Create();
SomeClassB:=TJPEGImage.Create();
SomeClassC:=TFontDialog.Create(Self);
Try
      Try
            SomeClassC.Execute();
            SomeClassA.LoadFromFile('ololo');
            SomeClassB.Assign(SomeClassA);
            // ...
      Except
            On E: Exception Do
                  ShowMessage('Exception: '+E.ToString);
            End;
Finally
      SomeClassA.Free();
      SomeClassB.Free();
      SomeClassC.Free();
      End;
End;



1



Блок try…except имеет следущий вид:


Оператор try..except

1702


Если исключение не инициируется во время выполнения списка операторов, то все операторы в списке будут выполняться последовательно до 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, которые можно использовать для передачи описания ошибки и идентификатора контекста для контекстной онлайн документации. Кроме того, этот класс объявляет конструкторы, позволяющие различными способами задавать описание и идентификатор контекста.

Глава 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

Понравилась статья? Поделить с друзьями:
  • Laravel логирование ошибок
  • Laravel логи ошибок
  • Laravel 500 ошибка после переноса
  • Land rover discovery ограничение мощности ошибка
  • Land rover discovery 3 ошибка пневмы