Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 11:

Отладка и оптимизация программ. Отладка

Обработчики ошибок и вложенные вызовы процедур

К каждой процедуре может быть подключено несколько обработчиков ошибок. Этот факт был специально отражен в схеме процедуры, когда в этой лекции описывалась модель управления ошибками. При возникновении ошибки в процедуре только один из них может быть активным. Активным обработчиком является тот обработчик, который связан с охраняемым блоком, в теле которого возникла ошибка. Заметьте, несмотря на то, что подключенных обработчиков в процедуре может быть несколько, в момент возникновения ошибки в ней может не быть активного обработчика, если ошибка возникла вне охраняемых блоков.

В процессе вычислений одни процедуры могут вызывать другие. Поэтому в момент возникновения ошибки в стеке вызовов процедур могут находиться несколько процедур: C1, C2, …Cn. Каждая из этих процедур может иметь активный обработчик ошибок. Какой же из них будет применяться для обработки ошибки? Рассмотрим применяемую стратегию обработки. Итак, пусть есть непустой стек вызовов C1, C2, …Cn, где C1 это самый внешний вызов, а Cn - самый внутренний вызов. Обработка начинается подъемом по стеку вызовов. Если в Cn имеется активный обработчик ошибки, то он и получает управление, если его нет, то в стеке проверяется следующий по порядку вызов. Если ни один из вызовов C1 - Cn не имеет активного обработчика, то выполняется стандартная обработка с выдачей сообщения об ошибке и снятия приложения. Пусть Ck - это первый, найденный в стеке вызов, для которого существует активный обработчик, и который, как было сказано, получает управление. Обработчик Ck имеет две возможности:

  • Обработать ошибку.
  • Передать обработку ошибки обработчику, выше стоящему в стеке вызовов.

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

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

Структура обработчика ошибок

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

Как правило, каждый обработчик предназначен для обнаружения и исправления ошибок некоторого класса. Поэтому, когда он встречается с ошибкой другого класса, то он не способен ее обработать. В этом случае он передает эту функцию выше стоящему обработчику. Реализуется это тем, что обработчик вызывает метод Raise с тем же номером ошибки. При возбуждении ошибки в обработчике ошибки возобновится процесс подъема по стеку вызовов и управление сможет получить следующий активный обработчик ошибки, который в свою очередь, либо обработает ошибку, либо передаст ее вверх. Заметим, что возбуждение ошибки в обработчике ошибки может быть сознательным, но может быть и из-за того, что некорректно работает сам обработчик. В любом случае возбуждение ошибки приведет к подъему по стеку вызовов.

Типичный обработчик ошибок представляет собой оператор выбора Select, в котором каждый случай соответствует одной обрабатываемой ошибке, а для непредусмотренных ошибок происходит повторное их возбуждение и, тем самым, передача их вверх по стеку вызовов. Допустим, в охраняемом блоке процедуры ожидаются ошибки с кодами K1, K2, , Kn - обработчик ошибок этой процедуры может быть таким:

'ErrorHandler:
	Select Case Err.Number	' анализ кода ошибки.
		Case K1
		…		'обработка ошибки с кодом K1
		Case K2
		…		'обработка ошибки с кодом K2
		. . .
		Case Kn
		…		'обработка ошибки с кодом Kn
		Case Else
			'Передача управления обработчику,выше стоящему в стеке вызовов

			Dim intErrNum As Integer
			intErrNum = Err.Number	'номер ошибки
			Err.Clear			' чистка объекта Err.
			Err.Raise Number:= intErrNum	' повторное возбуждение ошибки
	End Select

Метод Raise здесь используется для повторения исходной ошибки. Если произойдет ошибка, отличная от ошибок с кодами K1, K2, , Kn, управление будет передано вверх по стеку вызовов другому активному обработчику, если таковой есть. Заметьте, перед вызовом метода Raise происходит чистка объекта Err.

Сделаем еще несколько замечаний об обработке ошибок в Office 2000:

  • Коды всех внутренних, перехватываемых ошибок можно найти в разделе справочной системы "Trappable Errors" (Перехватываемые ошибки).
  • Если ошибка выполнения возникла в некоем объекте вне VBA (например, в рабочей странице Excel) и не обработана этим объектом, а возвращена в VBA-программу, она будет автоматически преобразована VBA в ошибку с кодом 440, которая определена как "Automation Error" (Ошибка программирования объектов). Такую ошибку желательно сразу же обработать. Если же Вы хотите передать ее на обработку вверх в вызывающую процедуру, желательно возбудить ошибку со своим специальным номером, чтобы вызывающая процедура могла различать ошибки, возникающие в разных объектах.
  • Объекты Office 2000, кроме рассмотренных выше средств работы с ошибками, могут иметь дополнительные средства для их распознавания и обработки. Например, для диалоговых окон и элементов управления определено событие Error, позволяющее обрабатывать их специфические ошибки, которые не могут быть переданы в VBA. Информация об ошибках операций доступа к базам данных может быть получена с помощью объекта Error и семейства Errors из библиотеки объектов доступа к данным (DAO). Описание ошибки Microsoft Access или объекта доступа к данным можно получить по номеру ошибки методом AccessError.
Функция CvErr

При работе с процедурами стандартного модуля есть еще один способ для возврата кодов ошибок, определенных пользователем. Для этой цели можно использовать функцию CVErr, возвращающую значение типа Variant с подтипом Error, которое содержит код ошибки, указанный пользователем. В вызывающей процедуре с помощью булевой функции IsError можно проверить, является ли возвращенное значение ошибкой. В следующем примере генерируется ошибка 1999, если аргумент функции Func1 является нечисловым.

Function Func1(Number As Variant) As Variant
	If IsNumeric(Number) Then
		' Вычисление корректного результата.
		Func1 = Number * Number
	Else	'аргумент некорректен
		Func1 = CVErr(1999) ' возвращает код ошибки
	End If
End Function

Проверять корректность работы Func1 можно так.

Sub Testfunc1()
Dim res As Variant, arg As Variant
	arg = 12
	res = Func1(arg)
	If IsError(res) Then		'проверка ошибочности результата
		Debug.Print "Ошибка #: ", res, "аргумент : ", arg

	Else
		Debug.Print "Результат : ", res
	End If
	arg = "двенадцать"
	res = Func1(arg)
	If IsError(res) Then		'проверка ошибочности результата
		Debug.Print "Ошибка #: ", res, "аргумент : ", arg

	Else
		Debug.Print "Результат : ", res
	End If
End Sub
10.6.

Приведем результаты вычислений:

Результат :	144 
Ошибка #:	 Error 1999	аргумент :	двенадцать
полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.