Шаг 79.
Пример использования блоков try...except и try...finally

    На этом шаге мы рассмотрим пример использования рассмотренных блоков.

    Для демонстрации использования оператора обработки исключительных ситуаций приведем следующий пример.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Label6: TLabel;
    Label7: TLabel;
    Memo1: TMemo;
    Edit5: TEdit;
    Button1: TButton;
    Edit6: TEdit;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label8: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
var
  Switch:Integer;
{Счетчик Switch изменяет свое значение по циклу от 1 до 4  }
{и выполняет переключение между четырьмя демонстрационными }
{примерами по щелчку на  кнопке  "Следующий пример"        }

function GetCodeName(V : Variant): String;
{ Функция возвращает идентификатор кода вариантного типа}
type
  TypeCodeNames = array [$0000..$000D] of String;
const
  CodeNames: TypeCodeNames =
       ('varEmpty','varNull','varSmallint','varInteger', 'varSingle','varDouble',
        'varCurrency','varDate', 'varOleStr','varDispatch','varError','varBoolean',
        'varVariant','varUnknown');
var
  Code : Integer;
begin
  Code := VarType(V) and varTypeMask;
  case Code of
    $0000..$000D: Result:= CodeNames[Code];
           $0011: Result:='varByte';
           $0100: Result:='varString';
           $0FFF: Result:='varTypeMask';
           $2000: Result:='varArray';
    else Result :='varError';
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Op1, Op2 : Variant;
  Integer1,Integer2 : Integer;
  Double1, Double2 : Double;
  Currency1, Currency2 : Currency;
  TDateTime1, TDateTime2 : TDateTime;

procedure PrintOperands (Op1, Op2: Variant);
{ Процедура печатает идентификаторы кодов операндов }
{ вариантного типа и значения этих операндо в заданных}
{ полях Edit1, Edit2, Edit3 и Edit4.}
var
  CodeName, S : String;
begin
  CodeName := GetCodeName(Op1);
  Edit1.Text := CodeName;
  if CodeName <> 'varString' then Str(Op1:10:4, S)
  else S := Op1;
  Edit2.Text := S;
  CodeName := GetCodeName(Op2);
  Edit3.Text := CodeName;
  if CodeName <> 'varString' then Str(Op2:10:4, S)
  else S := Op2;
  Edit4.Text := S;
end;

procedure Action1 (Op1,Op2: Variant);
{Процедура Actionl  печатает  текст  своих операторов  в  поле   }
{Memol,   выполняет операции сложения и деления над заданными    }
{операндами  Opl  и Oр2   вариантного  типа,   а  также  выводит }
{результаты этих операций в диалоговых окнах. В случае           }
{возникновения исключительной ситуации EVariantError             }
{сообщает об этом в диалоговом окне. При возникновении           }
{любых других исключений выводит сообщение "Ошибка               }
{ времени выполнения".                                           }
var
  Res : Variant;
  S   : String;
begin
  try
    { Вывод текста своих операторов в поле Memol. }
    Memo1.Lines[0]:='try';
    Memo1.Lines[1]:='   Res := Op1 + 0p2;';
    Memo1.Lines[2]:='   Str (Res:10:4, S);';
    Memo1.Lines[3]:='   MessageDlg(''Op1 + Op2 = ''+S, '+
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[4]:='   Res := Op1 / Op2;';
    Memo1.Lines[5]:='   Str (Res:10:4, S);';
    Memo1.Lines[6]:='   MessageDlg(''Op1 / Op2 = ''+S, ' +
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[7]:='except';
    Memo1.Lines[8]:='   on  EVariantError do';
    Memo1.Lines[9]:='  MessageDlg(''Ошибка EVariantError'', '+
        'mtError,[mbOk],0);';
    Memo1.Lines[10]:='  else';
    Memo1.Lines[11]:='  MessageDlg(''Ошибка времени '+
        'выполнения'' , mtError, [mbOK] , 0); ' ;
    Memo1.Lines[12]:='end;';
    Edit6.Text:='+';
    Res := Op1 + Op2;
    Str (Res:10:4, S);
    MessageDlg('Op1 + Op2 = '+S, mtInformation,[mbOk],0);
    Edit6.Text:='/';
    Res := Op1 / Op2;
    Str (Res:10:4, S);
    MessageDlg('Op1 / Op2 = '+S, mtInformation,[mbOk],0);
  except
    on EVariantError do
        MessageDlg('Ошибка EVariantError', mtError,[mbOk],0);
  else
        MessageDlg('Ошибка времени выполнения', mtError,[mbOk],0);
  end;
end;

procedure Action2 (Op1,Op2: Variant);
{Работает аналогично процедуре Actionl,   но при  возникно-     }
{вении любой исключительной  ситуации  выводит  одно и  то же   }
{ сообщение  "Какая-то ошибка  времени  выполнения".            }
var
  Res : Variant;
  S   : String;
begin
  try
    { Вывод текста своих операторов в поле Memol. }
    Memo1.Lines[0]:='try';
    Memo1.Lines[1]:='   Res := Op1 + 0p2;';
    Memo1.Lines[2]:='   Str (Res:10:4, S);';
    Memo1.Lines[3]:='   MessageDlg(''Op1 + Op2 = ''+S, '+
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[4]:='   Res := Op1 / Op2;';
    Memo1.Lines[5]:='   Str (Res:10:4, S);';
    Memo1.Lines[6]:='   MessageDlg(''Op1 / Op2 = ''+S, ' +
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[7]:='except';
    Memo1.Lines[8]:='  MessageDlg(''Какая-то ошибка времени '+
        'выполнения'' , mtError, [mbOK] , 0); ' ;
    Memo1.Lines[9]:='end;';
    Edit6.Text:='+';
    Res := Op1 + Op2;
    Str (Res:10:4, S);
    MessageDlg('Op1 + Op2 = '+S, mtInformation,[mbOk],0);
    Edit6.Text:='/';
    Res := Op1 / Op2;
    Str (Res:10:4, S);
    MessageDlg('Op1 / Op2 = '+S, mtInformation,[mbOk],0);
  except
    MessageDlg('Какая-то ошибка времени выполнения', mtError,[mbOk],0);
  end;
end;

procedure Action3 (Op1,Op2: Variant);
{По структуре аналогична процедуре Action2, но демонстри- }
{рует обработку исключительных ситуаций с помощью блока   }
{try. . . finally. Сообщение "Обработка блока операторов  }
{finally" будет появляться как нормальном выполнении      }
{операции деления, так и при возникновении любой	  }
{исключительной ситуации.                                 }
var
  Res : Variant;
  S   : String;
begin
  try
    { Вывод текста своих операторов в поле Memol. }
    Memo1.Lines[0]:='try';
    Memo1.Lines[1]:='   Res := Op1 / Op2;';
    Memo1.Lines[2]:='   Str (Res:10:4, S);';
    Memo1.Lines[3]:='   MessageDlg(''Opl / Op2 = ''+S, ' +
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[4]:='finally';
    Memo1.Lines[5]:='  MessageDlg(''Обработка блока операторов '+
        'finally'' , mtInformation, [mbOK] , 0); ' ;
    Memo1.Lines[6]:='end;';
    Edit6.Text:='/';
    Res := Op1 / Op2;
    Str (Res:10:4, S);
    MessageDlg('Opl / Op2 = '+S, mtInformation,[mbOk],0);
  finally
    MessageDlg('Обработка блока операторов finally', mtInformation,[mbOk],0);
  end;
end;

procedure Action4 (Op1,Op2: Variant);
{По структуре аналогична процедуре Action2, но демонстри-   }
{рует обработку исключительных ситуаций с помощью вложенных }
{блоков try try ... except ... end; finally ... end.        }
{ Сообщение "Обработка блока операторов finally" будет      }
{ появляться как нормальном выполнении операции деления,    }
{ так и после обработки любой исключительной ситуации при   }
{ ее возникновении.                                         }
var
  Res : Variant;
  S   : String;
begin
  try
   try
    { Вывод текста своих операторов в поле Memol. }
    Memo1.Lines[0]:='try';
    Memo1.Lines[1]:=' try';
    Memo1.Lines[2]:='   Res := Op1 / Op2;';
    Memo1.Lines[3]:='   Str (Res:10:4, S);';
    Memo1.Lines[4]:='   MessageDlg(''Op1 / Op2 = ''+S, ' +
        'mtlnformation,[mbOk],0) ; ' ;
    Memo1.Lines[5]:='except';
    Memo1.Lines[6]:='  MessageDlg(''Какая-то ошибка времени '+
        'выполнения'' , mtError, [mbOK] , 0); ' ;
    Memo1.Lines[7]:='end;';
    Memo1.Lines[8]:='finally';
    Memo1.Lines[9]:='  MessageDlg(''Обработка блока операторов '+
        'finally'' , mtInformation, [mbOK] , 0); ' ;
    Memo1.Lines[10]:='end;';
    Edit6.Text:='/';
    Res := Op1 / Op2;
    Str (Res:10:4, S);
    MessageDlg('Op1 / Op2 = '+S, mtInformation,[mbOk],0);
  except
    MessageDlg('Какая-то ошибка времени выполнения', mtError,[mbOk],0);
  end;
  finally
    MessageDlg('Обработка блока операторов finally', mtInformation,[mbOk],0);
  end;
end;
begin {TForm1.Button1Click}
  case Switch of
    1: begin
         Inc(Switch);
         Form1.Caption := 'Блок try. ..except on...do...else...end';
         { Исходные данные для работы без ошибки. }
         Edit5.Text:= 'Работа try...except on...do...else...end,'+
                 ' если ошибки не было';
         Op1 := 4; Op2 := 2;
         PrintOperands (Op1, Op2);
         Action1(Op1, Op2);
         { Исходные данные для работы с ошибкой EVariantError. }
         Edit5.Text:='Работа try...except on...do...else...end,'+
             ' если была ошибка EVariantError';
         Op1 := 4; Op2 := 'A';
         PrintOperands (Op1, Op2);
         Action1(Op1, Op2);
         { Исходные данные для работы с другой ошибкой }
         { (деления на нуль). }
         Edit5.Text:='Работа try...except on...do...else...end,' +
                   ' если была другая ошибка ';
         Op1 := 4; Op2 := 0;
         PrintOperands (Op1, Op2);
         Action1(Op1, Op2);
       end;
    2: begin
         Inc(Switch);
         Form1.Caption :='Блок try...except...end';
         { Исходные данные для работы без ошибки. }
         Edit5.Text:='Работа try...except...end,'+
                  ' если ошибки не было';
         Op1 := 4; Op2 := 2;
         PrintOperands (Op1, Op2); Action2(Op1, Op2);
         { Исходные данные для работы с ошибкой EVariantError. }
         Edit5.Text:='Работа try...except...end,'+
                     ' если была ошибка EVariantError';
         Op1 := 4; Op2 := 'A';
         PrintOperands (Op1, Op2); Action2(Op1, Op2);
         { Исходные данные для работы с другой ошибкой }
         { (деления на нуль). }
         Edit5.Text:='Работа try...except...end,'+
                 ' если была другая ошибка ';
         Op1 := 4; Op2 := 0;
         PrintOperands (Op1, Op2); Action2(Op1, Op2);
       end;
    3: begin
         Inc(Switch);
         Form1.Caption :='Блок try...finally...end';
         { Исходные данные для работы без ошибки. }
         Edit5.Text:='Работа try...finally...end,'+
                  ' если ошибки не было';
         Op1 := 4; Op2 := 2;
         PrintOperands (Op1, Op2); Action3(Op1, Op2);
         { Исходные данные для работы с ошибкой (деление на нуль). }
         Edit5.Text:='Работа try...finally...end,'+
                     ' если была какая-либо ошибка';
         Op1 := 4; Op2 := 0;
         PrintOperands (Op1, Op2); Action3(Op1, Op2);
       end;
    4: begin
         Switch:=1;
         Form1.Caption :='Блок try try...except...end; finally... end';
         { Исходные данные для работы без ошибки. }
         Edit5.Text:= 'Работа try try. ..except...end;' +
                 ' finally...end, если ошибки не было';
         Op1 := 4; Op2 := 2;
         PrintOperands (Op1, Op2); Action4(Op1, Op2);
         { Исходные данные для работы с ошибкой (деление на нуль). }
         Edit5.Text:= 'Работа try try...except...end; ' +
              'finally...end, если была какая-либо ошибка';
         Op1 := 4; Op2 := 0;
         PrintOperands (Op1, Op2); Action4(Op1, Op2);
       end;
  end; {case}
end; {TForm1.Button1Click}

procedure TForm1.FormCreate(Sender: TObject);
var
  i:Byte;
begin
   for i:=0 to 12 do Memo1.Lines.Add(' ');
   Form1.Caption := 'Оператор try' ;
end;

initialization
    Switch:=1;

end.
Текст этого примера можно взять здесь.

    После старта проекта, использующего приведенный модуль, при щелчке на кнопке "Следующий пример" будут поочередно запускаться четыре примера, демонстрирующие рассмотренные в шагах 75 - 78 варианты блоков обработки исключительных ситуаций. Однако прежде, чем запускать этот проект, необходимо обратить внимание на следующее.


    Замечание. При отладке проектов, использующих операторы try, в интегрированной оболочке Delphi 6, необходимо выключить опцию Stop on Delphi Exceptions, доступ к которой можно получить с помощью команды Debugger Options меню Tools. Эта опция находится на вкладке Language Exceptions:


Рис.1. Отключение опции


    В противном случае, при возникновении исключительных ситуаций будут появляться системные сообщения об ошибках времени выполнения для всех исключений, невзирая на то, выполняется ли обработка данного исключения операторами try, или нет.

    Приведем сокращенный протокол работы этого проекта с комментариями. После старта проекта и щелчка на кнопке "Следующий пример" выполняются операторы первого примера по альтернативе оператора case при Switch=l (см. операторы метода TForm1.Button1Click). Первый вызов процедуры Action1 с корректными исходными данными отрабатывает нормально, показывая на экране диалоговые окна с резулътатами выполнения операций + и /. При втором вызове процедуры Action1 форма проекта принимает следующий вид:


Рис.2. Ошибка EVariantError

    Как видно, процедуре Action1 передаются фактические параметры, при обработке которых возникает исключительная ситуация EVariantError. Поэтому при выполнении первой же операции сложения управление передается в блок обработки исключений, на экране появляется соответствующее диалоговое окно с сообщением об ошибке (рисунок 2) и работа процедуры Action1 заканчивается, не дойдя до операции деления.

    При третьем вызове Action1 моделируется ситуация деления на нуль:


Рис.3. Ошибка при делении на нуль

    В блоке exception нет отдельной конструкции on...do, обращающей именно такие исключения, поэтому будут выполняться операторы альтернативы else.


    Замечание. Используя альтернативу else следует всегда помнить, что она будет получать управление при возникновении всех исключительных ситуаций, не описанных в других альтернативах блока exception, даже тех, о существовании которых вы можете не знать, отменяя при этом действие стандартных обработчиков. Поэтому применять ее следует только тогда, когда вы в этом абсолютно уверены.

    На этом первый пример заканчивается и для запуска второго требуется щелкнуть на кнопке "Следующий пример". Во втором примере (Switch=2) аналогично первому выполняется три вызова процедуры Action2, в которой демонстрируется работа сокращенного варианта блока обработки исключений вида try...except...end без конструкций on...do. Первый, корректный, вызов Action2 отрабатывает также, как и в предыдущем примере. При втором вызове Action2 форма проекта будет иметь такой вид:


Рис.4. Вторая форма

    Второй и третий вызовы Action2 моделируют такие же исключения, как и при вызове Action1. Однако в Action2 все исключительные ситуации будут обрабатываться одинаково. В результате при втором запуске Action2 появится диалоговое окно, изображенное на рисунке 4.

    После очередного щелчка на кнопке "Следующий пример" запускается третий пример (Switch=3), демонстрирующий работу блока обработки исключений вида try...finally...end. Операторы, стоящие после зарезервированного слова finally, будут выполняться как в случае отсутствия исключений, так и в случае их возникновения. Поэтому при корректных исходных данных для процедуры Action3 последовательно получим на экране такие диалоговые окна:


Рис.5. Диалоговые окна

    При втором вызове Action3, когда моделируется исключение типа деления на нуль, форма проекта примет вид:


Рис.6. Деление на нуль


    Замечание. Появление сообщения "Floating point division by zero" означает, что использование блока вида try...finally...end, в отличие от блока try...except...end, не блокирует системных сообщений о возникновении исключительных ситуаций.

    В случаях, если требуется выполнить группу заключительных операторов с помощью блока try...finally...end и одновременно подавить системную реакцию на исключения, используются вложенные и обработки исключительных ситуаций вида:

try
  try
     .   .   . 
  except
     .   .   . 
 end; 
finally
     .   .   . 
end;
или
try
  try 
     .   .   . 
  finally
     .   .   . 
  end; 
except
     .   .   . 
end;

    Работа вложенного блока первого из приведенных вида демонстрируется в четвертом примере (Switch=4) рассматриваемого проекта. В этом примере дважды вызывается процедура Action4, которая содержит такие же операторы, как и процедура Action3, но, в отличие от последней, расположенные внутри вложенного блока обработки исключений. При этом передаваемые процедуре параметры моделируют такие же ситуации, как и в третьем примере. Первый вызов Action4 с корректными для операции деления данными отрабатывает идентично первому вызову Action3. А после второго вызова Action4, когда форма проекта будет иметь вид:


Рис.7. Четвертый вариант с первым из диалоговых окон

на экране появятся последовательно такие диалоговые окна (первое - на рисунке 7):


Рис.8. Второе диалоговое окно

    Как видно из полученных результатов, вместо системного сообщения "Floating point division by zero" на экране отображается диалоговое окно с сообщением "Какая-то ошибка времени выполнения", которое выводится операторами блока except. Кроме того, поскольку блок except располагается внутри блока finally, то сообщение об ошибке появится перед сообщением "Обработка блока операторов finally", а не наоборот, как в третьем примере.

    На следующем шаге мы рассмотрим принудительное возбуждение исключительных ситуаций.




Предыдущий шаг Содержание Следующий шаг