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

От Delphi 4 к Delphi 5 Исключительные ситуации в приложении

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

Когда вы пишите программный код, то предполагаете, что могут возникнуть ситуации, когда пользователь, допустим, введет данные, разрушающие всю систему (компьютер просто зависнет). Обычная ситуация, например деление на нуль, если вы не позаботились о том, чтобы она миновала ваше приложение, приведет к краху системы. Или следующий пример: недостаточный объем свободной памяти, выход данных за пределы диапазона и т.д. также приводят к неблагоприятным последствиям. Когда вы сами пишите программу, то не застрахованы и от других ситуаций, приводящих к разрушению системы (рисунок 1). Такие ситуации называются исключительными.




Исключительная ситуация (exception)
– это состояние, возникающее в процессе выполнения программы из-за любых ошибок или ошибочных условий.

Хорошая программа должна работать безошибочно, без зацикливания и зависания. Вы сами можете ввести операторы, которые позволят нормально завершить приложение, когда обнаружится ошибка. Но в этом случае вам необходимо постоянно производить проверки, что приведет к увеличению объема вашего приложения. Лучший помощник в процессе обнаружения ошибок это взгляд со стороны. Т.е. необходимо, чтобы ваши знакомые поработали с приложением, возможно, они помогут вам обнаружить ошибки или предполагаемые места их возникновения. Но всего предусмотреть, конечно, невозможно.

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


Если приложение уже создано и запускается самостоятельно вне Delphi, то тут уже никто не поможет, если возникнет исключительная ситуация.

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

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

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


Структурная обработка исключительных ситуаций
- это совокупность операторов, позволяющих программисту, когда возникает ошибка (исключительная ситуация), связаться с кодом программы, подготовленным для обработки такой ошибки. Такая совокупность операторов как бы "охраняет" фрагмент кода программы и определяет обработчика ошибки, который вызывается, если что-то происходит не так в "охраняемом" участке кода.

В нашем случае понятие исключительной ситуации относится к языку Object Pascal и не нужно его путать с системными исключительными ситуациями ( hardware exceptions ), такими как General Protection Fault . Эти исключительные ситуации обычно используют систему прерываний и особые состояния компьютера для обработки критичной системной ошибки. Исключительные ситуации в Delphi не используют системных прерываний.

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

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

Структурная обработка исключительной ситуации замещает ручную обработку ошибок автоматической. В этом случае при обнаружении ошибки создается (raise) исключительная ситуация.

Специальный код, сгенерированный компилятором и встроенный в Run-Time Library (RTL), начинает поиск обработчика данной исключительной ситуации. Если один из обработчиков ошибок подходит по типу для возникшей в приложении исключительной ситуации, то программа переходит на его выполнение. Если нет подходящего обработчика, то поиск продолжается, и так может идти до тех пор, пока будет найден подходящий обработчик ошибок среди используемых по умолчанию обработчиков в RTL . Обработчики ошибок из RTL только показывают сообщение об ошибке и форсированно прекращают выполнение программы. Любая исключительная ситуация, которая осталась необработанной, приведет к прекращению выполнения приложения.

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


Модель исключительных ситуаций в Delphi.


Модель исключительных ситуаций в Object Pascal является невозобновляемой (non-resumable) . Когда возникает исключительная ситуация, вы уже не сможете вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая (resumable ) модель).

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


Синтаксис обработки исключительных ситуаций.


Рассмотрим синтаксис обработки исключительных ситуаций. С этой целью в язык Object Pascal добавлено новое ключевое слово – try, которое используется для обозначения первой части защищенного участка кода. Всего существует два типа защищенных участков:


try..except
и


try..finally


Первый тип используется для обработки исключительных ситуаций. Его синтаксис:


try

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

Statement 2;

...

except

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

on Exception2 do Statement;

...

else

StatementN; {обработка всех остальных ошибок (обработчик по умолчанию)}

end;


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


try

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

Statement2;

...

finally

StatementN; {эти операторы всегда выполняются}

end;


Исключительные ситуации в Object Pascal описываются классами. Каждый класс соответствует определенному типу исключительных ситуаций. В то время, когда в программе возникает исключительная ситуация, создается объект соответствующего класса, который переносит информацию об этой ситуации из места возникновения в место обработки.

Классы исключительных ситуаций Delphi образуют иерархию, корнем которой является класс Exeption. Все имена классов исключительных ситуаций начинаются с буквы E ( от слова Exception). Используя Browser Delphi, рассмотрим иерархию класса Exeption (рисунок 3) . Ниже приведено подробное описание операторов, позволяющих реагировать на возникающие ошибки, их вы можете использовать в своих приложениях для защиты от зависания.


try


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


except

on EAbort do {"молчаливое" исключение, предназначенное для намеренного прерывания вычислений и быстрого выхода из глубоко вложенных процедур и функций. Генерируется процедурой Abort }

on EAbstractError do {попытка вызвать абстрактный метод}


on EAccesViolation do
{приложение осуществило доступ к неверному адресу в памяти, обычно это обозначает, что программа обратилась за данными по неинициализированному указателю}

on ЕАггауЕггог do {ошибка манипулирования с потомками класса ТВаsеАггау: использование ошибочного индекса элемента массива, добавление слишком большого числа элементов в массив фиксированной длины, попытка вставки элемента в отсортированный массив}


on EAssertionFailed
do {попытка использования ложного выражения}

on EBitsError do {ошибка доступа к массиву булевых величин}

on ЕСаcheЕггог do {ошибка построения кэша в кубе решений}

on EHeapExeption do {класс EheapException, его потомки - EOutOfMemory и EinvalidPointe r - используются, чтобы оперировать при неудачном распределении динамической памяти и неправильных операциях с указателями}

on EOutOfMemory do {свободная оперативная память исчерпана}

on EOutOfResources do {свободных ресурсов нет}


on EInvalidPointer
do {попытка освободить недействительный указатель. Обычно это означает, что указатель уже освобожден}

on EInOutError do {ошибка доступа к файлу или устройству ввода вывода. Код ошибки содержится в поле ErrorCode. Значение кода ошибки: 2 - файл не обнаружен; 3 - неправильное файловое имя; 4 - слишком большой файл; 5 - доступ не возможен; 100 - EOF; 101 - диск полный; 106 - неправильный ввод}

on ElntError do {общий класс исключительных ситуаций целочисленной арифметики, от которого порождены классы EDivByZero, ERangeError, EIntOverFlow }

on EDivByZero do {попытка деления целого числа на нуль}

on ERangeError do {выход за границы диапазона целого числа или результата целочисленного выражения}

on EIntOverFlow do {переполнение в результате целочисленной операции}


on EmathError do
{общий класс исключительных ситуаций вещественной математики, от которого порождены классы EInvalidOp, EZerodivide, EOverflow, EUnderflow, EInvalidArgument }

on EInvalidOp do {неверный код операции вещественной математики}

on EZeroDivide do {попытка деления вещественного числа на нуль}

on EOverflow do {потеря старших разрядов вещественного числа в результате переполнения разрядной сетки}

on EUnderflow do {потеря младших разрядов вещественного числа в результате переполнения разрядной сетки}


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

on EExternalExeption do {исключительная ситуация операционной системы, которая не соответствует ни одному из стандартных классов исключительных ситуаций Delphi. Это, например, может быть исключительная ситуация, возникшая в DLL – библиотеке, разработанной на C ++ }


on EPrivilige do
{попытка выполнить привилегированную инструкцию процессора, на которую программа не имеет права}

on EStackOverflow do {стек приложения не может быть больше увеличен}

on EControlC do {во время работы консольного приложения пользователь нажал комбинацию клавиш Ctrl + C }

on EinvalidCast do {неудачная попытка приведения объекта к другому классу с помощью оператора as }

on EConvertError do { EConvertError - происходит в случае возникновения ошибки при выполнении функций StrToInt и StrToFloat, когда преобразование строки в соответствующий числовой тип невозможна}

on EVariantError do {невозможность преобразования варьируемой переменной из одного формата в другой}

on EPropReadOnly do {попытка установки значения свойства в объекте OLE Automation, которое доступно только по записи}


on EPropWriteOnly do
{попытка получения значения свойства в объекте OLE Automation, которое доступно только по записи}


end;

end.


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


Построение диалогового окна About ("О программе").


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

В среде Delphi диалоговые окна работают в одном из двух режимов: модальном (modal) или немодальном (modeless).


Модальные диалоговые окна служат для взаимодействия с пользователем в режиме неделимого действия. Они не позволяют переключаться на другие окна своего приложения до тех пор, пока работа с ними не будет завершена (однако это не мешает вам переключаться на другие приложения, например с помощью кнопок на Панели Задач Windows или нажатием сочетания клавиш Alt + Tab ). В модальном режиме выполняется большинство окон диалога.

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

Диалоговое окно About ("О программе") содержит многие приложения. Оно открывается по команде меню Help/About ("О программе"), выполняется в модальном режиме и предназначено для чисто информационных целей. Вы можете использовать уже готовое диалоговое окно "О программе". Для этого добавьте к имеющейся форме новую форму, воспользуйтесь командой меню Delphi File, далее New. Откроется диалоговое окно New Items с заготовками форм. Щелкните левой кнопкой мыши по вкладке Forms — перед вами откроется диалоговое окно, содержащее готовую форму About box (рисунок 5).

Добавьте в строку главного меню основной формы выпадающее меню Help с командой "О программе". По этой команде будет вызываться диалоговое окно About.


Вы можете отредактировать содержимое формы "О программе". Например, переименовать название формы на AboutForm и сохранить данный модуль под именем About_.pas. Можете придать форме другой размер и установить заголовок.

Модальные диалоговые окна обычно появляются в центре экрана. За это отвечает свойство формы Position. Найдите это свойство на странице Properties инспектора объекта; если установлено значение отличное от poScreenCenter, то установите его.

Если вам не нравится используемая картинка в разработанном окне, воспользуйтесь картинкой, созданной вами, измените замечания по авторским правам и так далее.

Итак, если визуальное изображение диалогового окна вас удовлетворяет, обеспечьте его вызов при выборе команды меню Help, далее команды "О программе".


Вначале необходимо обеспечить видимость формы AboutForm в главной форме. Используя команду меню View/ Project Manager, перейдите к главной форме. Выберите в меню File команду Use unit… . Перед вами появится диалоговое окно Use Unit (рисунок 6), выберите в нем модуль About_ и щелкните по кнопке Ok. Модуль About_, содержащий определение формы AboutForm, подключится к модулю главной формы. Чтобы убедиться в этом, необходимо перейти в Редактор кода главной формы, раздел implementation, вы увидите строку uses About_; эту строку можно набрать вручную.

Чтобы обеспечить выполнение диалога, для команды меню "О программе" реализуем обработчик события OnClick .


procedure TForm1.AboutClick(Sender: TObject);

begin

AboutForm.ShowModal;

end;

end.


Метод ShowModal производит запуск диалогового окна в модальном режиме и возвращает управление только после его завершения. В данном примере метод вызывается как процедура, но в действительности это функция, которая возвращает значение свойства формы ModalResult . Немодальный диалог происходит, когда вы используете метод Show.


Убедитесь, что приложение работает, для этого запустите его на компиляцию, нажав клавишу F9.


Рассмотрим интересный момент, когда и где конструируется объект AboutForm. Объект AboutForm создается сразу после старта приложения и существует во время всей его работы. В этом можете убедиться, если откроете файл проекта. В главном программном блоке, используя команды меню Project/View Source, вы увидите оператор Application.CreateForm(TAboutForm, AboutForm);. Он и обеспечивает автоматическое создание объекта формы. Это удобно, но не всегда, так как память, выделенная объекту, остается занятой даже тогда, когда форма невидима, т.е. до запуска диалога и после его завершения.

Для приложений, в которых немного форм, это не очень важно. Но если форм много, то от автоматического создания формы, исключая главную форму, лучше отказаться. Для исключения автоматического создания формы AboutForm, необходимо открыть диалоговое окно Project Options (рисунок 7) . Для исключения автоматического создания формы AboutForm необходимо открыть диалоговое окно Project Options на странице Forms. Выделите элемент AboutForm из списка Autocreate forms и переместите в список Available forms.


Теперь процесс создания формы ложится на ваши плечи.

Необходимо произвести изменения в программном коде.


procedure TForm1.AboutClick(Sender: TObject);

begin

TAboutForm.Create(Application);

try

AboutForm.ShowModal;

finally

AboutForm.Free;

end;

end;

end.


Теперь ресурс оперативной памяти используется более рационально.

В данном примере вы создали переменную-указатель в событии OnClick, которая указывает на экземпляр TAboutForm. Щелкнув по кнопке, вы создаете новый экземпляр TAboutForm и заставляете вашу переменную указывать на этот экземпляр. Затем вы вызываете метод Show для отображения окна формы.

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


Компонент StringGrid.


Компонент StringGrid предназначен для создания таблиц, в ячейках которых располагаются произвольные текстовые строки. Такой компонент можно использовать там, где требуется показать то, что представляет собой сетку, например электронные таблицы. Доступ к квадратам сетки осуществляется через свойство Cells (ячейки), которое представляет собой двумерный массив строк – по строке на ячейку. Значения в свойствах FixedCols и FixedRows определяют количество фиксированных колонок и строк. Это свойство необходимо использовать для ввода названий строк и/или колонок. Свойства RowCount и ColCount устанавливают количество строк и колонок, которое надо показывать в сетке. Можно также изменять размер каждой ячейки, устанавливая свойство DefaultColWidth (ширина колонки по умолчанию) и DefaultRowHeight (высота строки по умолчанию).

Рассмотрим пример использования компонента StringGrid для решения системы уравнений первой степени с двумя неизвестными.

Вначале вспомним теорию. Решение системы уравнений вида:

ax + by = c,

a 1 x + b 1 y = c1

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

Х =
;

Y =
.

Использование компонента StringGrid позволит получить наглядность решения примеров подобного вида.

    1. Для получения результата поставленной задачи запустите Delphi.
    2. Сохраните файл модуля под именем Equation_.pas, а файл проекта под именем Equation.dpr.
    3. Поместите на форму три компонента StringGrid со страницы Additional. Каждый компонент будем использовать для формирования определителей. Используя свойство RowCount и ColCount, установите значение 2 в обоих случаях для всех трех компонентов. Используя свойство FixedCols, FixedRows, установите значение 0 для всех трех компонентов. В данном примере не будем использовать фиксированные ячейки для заголовка. Остальные компоненты вы можете разместить по своему усмотрению или используя рисунок 8.

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


procedure TForm1.Button1Click(Sender: TObject);

begin

{определение ячейкам коэффициентов при неизвестных, формирование определителя "Дельта"}

StringGrid1.Cells[0,0]:= Edit1.Text;

StringGrid1.Cells[1,0]:= Edit2.Text;

StringGrid1.Cells[0,1]:= Edit4.Text;

StringGrid1.Cells[1,1]:= Edit5.Text;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

{определение ячейкам коэффициентов при неизвестных, формирование определителя "Дельта Х"}

StringGrid2.Cells[0,0]:= Edit3.Text;

StringGrid2.Cells[1,0]:= Edit2.Text;

StringGrid2.Cells[0,1]:= Edit6.Text;

StringGrid2.Cells[1,1]:= Edit5.Text;

end;

procedure TForm1.Button3Click(Sender: TObject);

begin

{присвоение ячейкам коэффициентов при неизвестных, формирование определителя "Дельта Y"}

StringGrid3.Cells[0,0]:= Edit1.Text;

StringGrid3.Cells[1,0]:= Edit3.Text;

StringGrid3.Cells[0,1]:= Edit4.Text;

StringGrid3.Cells[1,1]:= Edit6.Text;

end;

procedure TForm1.Button4Click(Sender: TObject);

var

X,Y:Real;

B1,C,C1,B,A,A1:Integer;

Begin

{защита блока расчета значений Х и Y от ошибки деления на нуль}

try

A:=StrToInt(Edit1.Text);

B:=StrToInt(Edit2.Text);

C:=StrToInt(Edit3.Text);

A1:=StrToInt(Edit4.Text);

B1:=StrToInt(Edit5.Text);

C1:=StrToInt(Edit6.Text);

X:=(B1*C - B*C1)/(A*B1 - A1*B);

Y:=(A*C1 - A1*C)/(A*B1 - A1*B);

Edit7.Text:=FloatToStr(X);

Edit8.Text:=FloatToStr(Y);

except

on EDivByZero do

end;

end;

end.


Запустите приложение на компиляцию.

Чтобы оценить работу блока защиты от ошибок, введите для расчета следующую систему уравнений:


10Х + 6Y = 18,

5X + 3Y = 9


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

Литература:

  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) компьютерная газета


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

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