От Delphi 4 к Delphi 5 часть 25

От Delphi 4 к Delphi 5
Обработка исключительных ситуаций в блоках try…except


На рисунке 1 приведена иерархия стандартных классов исключительных ситуаций (для просмотра воспользуемся командой меню View, далее Browser Delphi 4 ), объявленных в модуле SysUtils. Они покрывают практически весь спектр возможных ошибок. Если вам необходимо, то можете объявить новые классы исключительных ситуаций, порожденные от класса Exception или его наследников. На рисунке 2 показан процесс просмотра иерархии стандартных классов исключительных ситуаций в Delphi 5.




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

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


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


try

{защищенные от ошибок операторы}

except

{операторы обработки исключительной ситуации}

end;


Распознавание исключительной ситуации выполняется с помощью конструкций


on < класс исключительной ситуации > do < оператор >;


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

Рассмотрим, как работает защита приложения, на конкретном примере.

    1. Запустите Delphi.
    2. Запомните файл модуля под именем Er_.pas, а файл проекта под именем Er.dpr.
    3. Поместите на форму два компонента Edit и компонент Button.
    4. Создайте сами исключительную ситуацию, произведите деление на нуль.

Программный код выглядит следующим образом:


procedure TForm1.Button1Click(Sender: TObject);

var

X,Y:Real;

begin

try

X := StrToFloat(Edit1.Text);

Y:= StrToFloat(Edit2.Text);

X:=X/Y;

Edit1.Text:=FloatToStr(X);

except

on EmathError do

end;

end;

end.


После того как введете в знаменатель нуль и щелкните по кнопке, произойдет генерация исключительной ситуации и вы получите сообщение (рисунок 3), когда запуск приложения осуществляется из Delphi.


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

Для этого вам необходим доступ к объекту, описывающему эту исключительную ситуацию и содержащему код ошибки, текстовое описание ошибки и т.д.

В этом случае используется расширенная запись оператора on:

on < идентификатор объекта > : < класс исключительной ситуации > do < оператор >;


Измените программный код для нашего примера с целью получения сообщения об ошибке при запуске приложения вне Delphi:

except

on E:EmathError do ShowMessage(E.Message);

end;


где переменная Е объект исключительной ситуации.


ShowMessage –
процедура модуля Dialogs, отображающая на экране окно с текстом и кнопкой ОК.


Свойство Message типа String определено в классе Exception, оно содержит текстовое описание ошибки. Исходное значение для текста указывается при конструировании объекта исключительной ситуации.

После обработки исключительной ситуации освобождение соответствующего объекта выполняется автоматически.

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


Перепишем программный код для выдачи собственного сообщения:


on EmathError do

MessageDlg ('Произошло деление на нуль; повторите ввод',

mtWarning, [mbOk],0);


На рисунке 5 показано сообщение, созданное для этого случая.

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

Например, имеется класс исключений EMathError, производный от EExternal и являющийся родительским для ряда исключений ElnvalidOp, EZeroDivide и т.д. Поэтому можно обрабатывать сразу группу родственных исключений. Так вы и сделали в вышеприведенном примере. Оператор on EmathError do перехватывает исключения всех классов, порожденных от EmathError.


В разделе except может быть включен оператор else, в котором выполняется обработка всех не перехваченных ранее исключений, т.е. происходит обработка по умолчанию.


except

on EZeroDivide do

on EmathError do

else

end;


Разработчики Delphi предупреждают, что обработкой исключительных ситуаций по умолчанию лучше не пользоваться, поскольку все приложения строятся на основе библиотеки Visual Component Library, в которой обработка по умолчанию уже предусмотрена. Кроме того, перехват всех исключений способен замаскировать какие-то непредвиденные ошибки в программе, что затруднит их поиск.

Рассмотрим более подробно операторы, позволяющие реагировать на возникающие ошибки.


try


{защищенные от ошибок операторы}


except

on EdatabaseError do
{ошибка работы с базами данных}


on EDBClient do
{ошибка в наборе данных клиента. Свойство ЕггогСо de содержит код ошибки, возвращаемый В }


on
EReconcileЕггог do {ошибка обновления данных компонента TclientDataset; свойство Со ntext содержит информацию в виде сообщения об ошибке}


on EDBEngineError do
{ошибка в В DЕ. Свойство Еггог s содержит информацию об ошибке — объект типа TBDErrors. Свойство ЕггогС ount хранит число ошибок}

on EDBEditError do {вызывается, когда данные не совместимы с маской ввода, наложенной на поле}

on ENoResultSet do {генерируется компонентом ТО uегу при попытке открыть запрос без оператора Select }


on EUpdateError do
{ошибка при обновлении в ТР rovider }

on EDateTimeError do {ошибка ввода даты или времени в компоненте TDateTime Picker }

on EDimUndexError do {ошибочный индекс в задании размерности в кубе решений}


on EIntCastError do
{ошибочное применение операции преобразования типов а s к интерфейсу}

on EInvalidGraphic do {нераспознаваемый графический файл}

on EInvalidGraphicOperation do {ошибочная операция с графикой}

on EInvalidGridOperation do {ошибочная операция с таблицей}

on EInvalidOperation do {ошибочная операция с компонентом; генерируется при попытке выполнить операцию, которая требует обработчика окна, над компонентом, не имеющем родителя (свойство Раге nt = nil ). Это исключение также генерируется при выполнении операций перетаскивания над формой (например, при попытке выполнить операцию Form1.BeginDrag }


on EListError do
{ошибка работы с объектом типа списка TStringList и TList: попытка сослаться на элемент с индексом вне допустимых пределов, попытка добавления дубликата строки в объект TStringList, в котором значение свойства Duplicates равно dupError, попытка вставить элемент в сортированный список, так как это может нарушить правильную последовательность элементов}

on EInvalidOp do {происходит, когда математическому сопроцессору передается ошибочная инструкция. Такое исключение не будет до конца обработано, пока вы контролируете сопроцессор напрямую из ассемблерного кода}

on ERangeError do {вызывается при попытке обращения к элементам массива по индексу, выходящему за пределы массива, как результат RunTime Error 201 при включенной директиве {$R+}}

on Underflow do {происходит как результат переполнения операций с плавающей точкой при слишком малых величинах. Соответствует RunTime Error 206 }

on EMenuError do {вызывается в случае любых ошибок при работе с пунктами меню для компонент TMenu, TMenuItem, TPopupMenu и их наследников}

on EOutlineError do {вызывается в случае любых ошибок при работе с TOutLine и любыми его наследниками}

on EParserError do {вызывается когда Delphi не может произвести разбор и перевод текста описания формы в двоичный вид (часто происходит в случае исправления текста описания формы вручную в IDE Delphi) }


on EPrinter do
{вызывается в случае любых ошибок при работе с принтером}

on EProcessorException do {предок исключений, вызываемых в случае прерывания процессора — hardware breakpoint . Никогда не включается в DLL, может обрабатываться только в "цельном" приложении}

on EBreakpoint do {вызывается в случае останова на точке прерывания при отладке в IDE Delphi . Среда Delphi обрабатывает это исключение самостоятельно}

on EFault do {предок исключений, вызываемых в случае невозможности обработки процессором тех или иных операций}

on EGPFault do {вызывается, когда происходит "общее нарушение защиты" - General Protection Fault . Соответствует RunTime Error 216 }

on EInvalidOpCode do {вызывается, когда процессор пытается выполнить недопустимые инструкции}

on EPageFault do {обычно происходит как результат ошибки менеджера памяти Windows вследствие некоторых ошибок в коде вашего приложения. После такого исключения рекомендуется перезапустить Windows}

on EStackFault do {происходит при ошибках работы со стеком, часто вследствие некорректных попыток доступа к стеку из фрагментов кода на ассемблере. Компиляция ваших программ со включенной проверкой работы со стеком {$S+} помогает отследить такого рода ошибки}

on EPropertyError do {вызывается в случае ошибок в редакторах свойств, встраиваемых в IDE Delphi}

on EStreamError do {предок исключений, вызываемых при работе с потоками}

on EFCreateError do {происходит в случае ошибок создания потока (например, при не корректном задании файла потока}

on ElowCapacityError do {попытка выделить памяти больше, чем доступно кубу решений; надо или увеличить значение Capacity, или уменьшить размерность куба}

on EInvalidArgument do {недопустимое значение параметра при обращении к математической функции}

on EMCIDeviceError do {ошибка доступа к устройствам мультимедиа через драйвер Media Control InterFace (MCI) }


on EOleCtrlError do
{генерируется при невозможности связать приложение с компонентом ActiveX }

on EOleError do {низкоуровневая ошибка OLE ; Delphi проверяет это исключение, но не генерирует его}

on OleSysError do {ошибка OLE, специфическая для интерфейса OLE IDispatch ; свойство Еггог Code содержит номер ошибки}


on EOleException do
{ошибка OLE, связанная с методом или свойством}


on EPackageError do
{исключение времени проектирования, генерируемое при загрузке или использовании пакета}

on EReportError do {ошибка задания типа сервера, из-за которой компонент Report не может соединиться с базой данных}

on EResNotFound do {ошибка при загрузке файла ресурсов . dfm или .res в процессе проектирования}

on ETreeViewError do {ошибка индекса при работе с компонентом TreeView }

on EUnsupportedTypeError do {ошибка выбора типа поля в качестве размерности куба решений}

on EWin32Error do {ошибка Windows }


end;


Компонент StringGrid.


Рассмотрим свойства компонента StringGrid.


Свойство BorderStyle определяет рамку компонента. Стиль bsNone предполагает отсутствие рамки. Стиль bsSingle определяет рамку толщиной в 1 пиксель.

Свойство Cells определяет содержимое ячейки с табличными координатами.

Свойство Col содержит номер столбца сфокусированной ячейки.

Свойство ColCount содержит количество столбцов таблицы. Задавая числовые значения, вы определяете количество столбцов таблицы.

Свойство Cols содержит все строки колонки с индексом Index.


Свойство ColWidths содержит ширину столбца с индексом Index.


Свойство DefaultColWidth содержит умалчиваемое значение ширины столбца, равное 64.

Свойство DefaultDrawing разрешает или запрещает автоматическую прорисовку служебных элементов таблицы – фиксированной зоны, фона и прямоугольника сфокусированной ячейки.

Свойство DefaultRowHeight содержит умалчиваемую высоту рядов, равную 24.

Свойство EditorMode разрешает или запрещает редактирование ячеек. Игнорируется, если свойство Options включает goAlwayseShowEditor или не включает goEditing.


Свойство FixedColor определяет цвет фиксированной зоны.

Свойство FixedCols определяет количество столбцов фиксированной зоны.

Свойство FixedRows определяет количество рядов фиксированной зоны.

Свойство GridHeight содержит высоту таблицы.

Свойство GridLineWidth определяет толщину линий, расчерчивающих таблицу.

Свойство GriWidth содержит ширину таблицы.

Свойство LeftCol содержит номер самого левого столбца, видимого в зоне прокрутки.

Свойство Objects обеспечивает доступ к объекту, связанному с ячейкой ( ACol, ARow ).

Свойство Options содержит параметры таблицы.

Свойство TabStops разрешает или запрещает выбирать столбец с индексом Index при обходе клавишей Tab. Игнорируется в случае, если свойство Options не содержит значение True в свойстве goTabs.


Рассмотрим пример использования компонента StringGrid. Очень удобно его применять при работе с определителями. Определитель получается из элементов матрицы по соответствующим формулам. Для примера используем определитель третьего порядка. Данное приложение поможет вам изучить простейшие свойства определителей.

    1. Запустите Delphi. Сохраните файл модуля под именем Finder_.pas, а файл проекта под именем Finder.dpr.
    2. Поместите на форму компонент StringGrid. Установите свойства RowCount, ColCount компонента StringGrid1 равные 4. Крайние ячейки слева и справа оставим для обозначения крайних элементов матрицы будущего определителя. Свойство FixedCols, FixedRows оставим без изменения равным 1. Используя свойство Font, установим размер шрифта 12, начертание полужирное. Используем событие OnCreate формы, напишем программный код, осуществляющий надпись на крайних элементах матрицы.

    3. procedure TForm1.FormCreate(Sender: TObject);

      begin

      StringGrid1.Cells[1,0]:=' A11';

      StringGrid1.Cells[2,0]:=' A12';

      StringGrid1.Cells[3,0]:=' A13';

      StringGrid1.Cells[0,1]:=' A11';

      StringGrid1.Cells[0,2]:=' A21';

      StringGrid1.Cells[0,3]:=' A31';

      end;

    4. Не снимая выделения с компонента StringGrid1, на странице Properties найдите комплексное свойство Options. Раскройте его и для вложенного свойства goEditing установите значение True. Это даст возможность вводить числовые параметры в ячейки.
    5. Установите значение goTabs в True для возможности перемещения по ячейкам с использованием клавиши табуляции.
    6. Выделите компонент StringGrid1, скопируйте его в буфер обмена и поместите его на форму. Вы увидите на форме компонент StringGrid2, который будем использовать для отображения транспонированной матрицы.
    7. Поместите на форму кнопку Button1, щелчок по кнопке будет приводить к транспонированию матрицы, значения которой вы введете в компонент StringGrid1. Транспонирование матрицы (а также определителя) есть перемена ролями строк и столбцов с сохранением их номеров.

    8. procedure TForm1.Button1Click(Sender: TObject);

      begin

      StringGrid2.Cells[2,1] := StringGrid1.Cells[1,2];

      StringGrid2.Cells[3,1] := StringGrid1.Cells[1,3];

      StringGrid2.Cells[3,2] := StringGrid1.Cells[2,3];

      StringGrid2.Cells[1,1] := StringGrid1.Cells[1,1];

      StringGrid2.Cells[2,2] := StringGrid1.Cells[2,2];

      StringGrid2.Cells[3,3] := StringGrid1.Cells[3,3];

      StringGrid2.Cells[1,2] := StringGrid1.Cells[2,1];

      StringGrid2.Cells[1,3] := StringGrid1.Cells[3,1];

      StringGrid2.Cells[2,3] := StringGrid1.Cells[3,2];

      end;


      Для матриц с порядком N больше 3 можете организовать преобразование строк и столбцов в цикле.

    9. Поместите на форму компонент Button2, щелчок по кнопке позволит произвести расчет определителя третьего порядка по правилу Саррюса (правилу треугольников), рассчитанное значение поместите в строку TexOut.

Определитель третьего порядка рассчитывается по следующей формуле:


А = А11*А22*А33 + А13*А23*А31 + А21*А32*А13 - А13*А22*А31 - А12*А21*А33 -А23*А32*А11.


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

Procedure Val (St: String; var X; Code:Integer);
преобразует строку символов St во внутреннее представление целой или вещественной переменной X, которое определяется типом этой переменной. Параметр Code содержит ноль, если преобразование прошло успешно, и тогда в X помещается результат преобразования, в противном случае он содержит номер позиции в строке St, где обнаружен ошибочный символ, и в этом случае содержимое X не меняется. В строке St могут быть ведущие и/или ведомые пробелы.

Программный код представлен ниже:


procedure TForm1.Button2Click(Sender: TObject);

var

I,J,K,L,M,N,P,R,S, Code: Integer;

begin

Val(StringGrid1.Cells[1,1], I, Code);

Val(StringGrid1.Cells[2,2], J, Code);

Val(StringGrid1.Cells[3,3], K, Code);

Val(StringGrid1.Cells[1,2], L, Code);

Val(StringGrid1.Cells[2,3], M, Code);

Val(StringGrid1.Cells[3,1], N, Code);

Val(StringGrid1.Cells[2,1], P, Code);

Val(StringGrid1.Cells[3,2], R, Code);

Val(StringGrid1.Cells[1,3], S, Code);

{ Анализ результата преобразования }

if code > 0 then

MessageDlg('Ошибка в позиции: ' + IntToStr(Code), mtWarning, [mbOk], 0)

else

I:=I*J*K + L*M*N + P*R*S - S*J*N - L*P*K - M*R*I;

Canvas.TextOut(300, 10, 'Определитель = ' + IntToStr(I));

end;


На рисунке 6 показан результат исследования некоторых свойств матрицы.


Литература:

    1. Марко Канту. Delphi 2 для Windows 95/NT. Москва. ООО "Малип". 1997 г.
    2. Джон Матчо. Дэвид Р. Фолкнер. Delphi. Москва. БИНОМ. 1995 г.
    3. Эндрю Возневич. Delphi. Освой самостоятельно. Москва. Восточная книжная компания. 1996 г.
    4. К. Сурков, Д. Сурков, А. Вальвачев. Программирование в среде Delphi 2.0 Минск. ООО "Попурри" 1997 г.
    5. Джозеф Когсвел. Изучи сам Delphi 2. 0 сегодня. Минск. ООО "Попурри". 1997 г.
    6. В.В.Фаронов. Delphi 5 Учебный курс. Москва. Издательство Нолидж. 2000 г.
    7. А. Я. Архангельский. Программирование в Delphi 5. Москва. ЗАО "Издательство Бином". 2000 г.
Владимир Скуратов



(c) компьютерная газета


Компьютерная газета. Статья была опубликована в номере 34 за 2001 год в рубрике программирование :: delphi

©1997-2024 Компьютерная газета