При составлении приложений важно предусмотреть, чтобы программа анализировала возможные ошибки,
возникающие при ее выполнении по вине пользователя, и информировала его об этом,
подсказывая, что конкретно он сделал неправильно. При этом возможно два подхода:
Следующая
программа производит деление числителя на знаменатель по нажатию кнопки Счет
без контроля появления возможных ошибок:
Private Sub
CommandButtonl_Click()
Dim Числитель,
Знаменатель, Результат As Double
Числитель = CDbl(TextBoxl.Text)
Знаменатель = CDbl(TextBox2.Text)
Результат = Числитель / Делитель
TextBoxS.Text = CStr(Результат)
End Sub
Рис.
12.1. Диалоговое окно Деление
Несмотря
на то что рассматриваемая ситуация очень простая, она уже таит в себе множество
подводных камней. Например, если пользователь по невнимательности забудет ввести
в поле числитель или в поле Знаменатель число при нажатии кнопки счет происходит
аварийное прерывание программы с малопонятным сообщением о несоответствии типов
отображаемом в диалоговом окне Microsoft Visual Basic (рис. 12.2)
Рис.
12.2. Диалоговое окно Microsoft Visual Basic с сообщением
об ошибке
Данное сообщение
об ошибке связано с одной из следующих двух инструкций в программе:
Числитель = CDbl(TextBoxl.Text)
Знаменатель
= CDbl(TextBox2.Text)
где аргументом
функции соы должна быть строка, преобразуемая в число. Если в какое-то из полей,
числитель или Знаменатель, ничего не введено, по умолчанию из этого поля будет
считываться пустая строка. Но пустая строка не может быть преобразована в число,
и поэтому из-за функции CDbl происходит ошибка. Ошибка о несоответствии типов
возникнет также, если в одно из полей пользователь по неосторожности введет
число с десятичной запятой, а установками системы предусматривается десятичная
точка и наоборот.
Такие ошибки
ввода легко избежать, если производить в программе предварительную проверку:
преобразуются ли вводимые данные в числа? Эту предварительную проверку можно,
например, сделать, как показано в следующей процедуре, в которой предусмотрены:
Рис.
12.3. Пример сообщения о некорректном вводе данных
Знаменатель
было введено число с десятичной запятой, в то время как установки системы предусматривают
десятичную точку. Поэтому процедура вызвала отображение диалогового окна деление
с сообщением об ошибке в знаменателе и установила фокус на поле знаменатель.
Private Sub
CoramandButtonl_Click()
Dim Числитель, Знаменатель, Результат As Single
'
If IsNumeric(TextBoxl.Text)
= False Then MsgBox "Ошибка в числителе",
vblnformation, "Деление" TextBoxl.SetFocus
Exit Sub
End If
'
If IsNumeric(TextBox2.Text)
= False Then
MsgBox "Ошибка
в знаменателе",
vblnformation,
"Деление"
TextBox2.SetFocus
Exit Sub
End If
'
Числитель = CDbl(TextBoxl.Text)
Знаменатель = CDbl(TextBox2.Text)
Результат = Числитель / Делитель
TextBox3.Text = CStr(Результат)
End Sub
Но это еще
не все подводные камни, которые подстерегают неосторожного пользователя при
вводе данных даже в этом простом примере. Если пользователь в поле Знаменатель
введет 0, то также произойдет аварийная остановка выполнения программы с отображением
в диалоговом окне Microsoft Visual Basic сообщения: Деление на 0.
Для избежания подобной ошибки будем проверять не только, являются ли введенные
в поле данные числом, но и что это не ноль. Например, добавим перед расчетным
блоком в процедуре следующую дополнительную проверку:
If CDbl(TextBox2.Text)
= 0 Then
MsgBox "Знаменатель
не может быть нулем", vblnformation, "Деление"
TextBox2.SetFocus
Exit Sub
End If
Из диалогового
окна Microsoft Visual Basic видно, что каждая ошибка имеет свой код.
В табл. 12.1 приведены коды наиболее часто встречаемых ошибок.
Таблица
12.1. Коды наиболее часто встречаемых ошибок
Код |
Сообщение |
||
5 |
Приложение не
запущено |
||
6 |
Переполнение |
||
7 |
Не хватает памяти |
||
9 |
Индекс выходит
за пределы допустимого диапазона |
||
11 |
Деление на нуль |
||
13 |
Несоответствие
типа |
||
18 |
Произошло прерывание,
вызванное пользователем |
||
52 |
Неправильное
имя файла или идентификатор |
||
53 |
Файл не найден |
||
54 |
Неверный режим
работы с файлом |
||
55 |
Файл уже открыт |
||
56 |
Ошибка ввода-вывода |
||
61 |
Переполнение
диска |
||
68 |
Устройство недоступно |
||
71 |
Диск не готов |
||
72 |
Повреждена поверхность
диска |
||
335 |
Невозможен доступ
к системным ресурсам |
||
368 |
Истек срок действия
данного файла. Программе требуется файл более новой версии |
||
482 |
Ошибка принтера |
||
В идеале
разрабатываемое приложение не должно никогда аварийно прерываться. В приложении
следует создать средства перехвата любой возможной ошибки, обработать ее, выдать
сообщение пользователю и обеспечить безаварийное продолжение работы приложения.
Обычно конструкция перехвата ошибок имеет следующую структуру.
Инструкция On Error |
Производит перехват
ошибки. Устанавливает, что про грамма должна делать в случае появления
ошибки. |
||
Подпрограмма
обработки ошибки |
В этой подпрограмме определяется тип возникшей ошибки и устанавливается, что программа должна де-лать в зависимости от типа ошибки. Допустимы следующие синтаксисы. Синтаксис 1: On Error GoTo строка Активизирует
подпрограмму обработки ошибок, начало которой определяется обязательным
аргументом строка, значением которого может быть либо метка строки,
либо номер строки. Синтаксис 2: On Error Resume Next Указывает, что при возникновении ошибки происходит передача управления на инструкцию, непосредственно следующую за инструкцией, где возникла ошибка. Синтаксис 3: On Error GoTo 0 Отключает любой
активизированный обработчик ошибок в текущей процедуре. |
||
Инструкция Resume |
Обеспечивает процедуре возможность продолжить работу после обработки ошибки. Допустимы следующие синтаксисы. Синтаксис 1: Resume После обработки ошибки управление передается той инструкции, в которой произошла ошибка. Синтаксис 2: Resume строка После обработки
ошибки управление передается инструкции, определенной аргументом строка.
Значением этого аргумента может быть любая метка строки или номер
строки. Синтаксис 3: Resume Next После обработки
ошибки управление передается инструкции, следующей за инструкцией,
в которой произошла ошибка. |
||
Инструкция Exit |
Останавливает выполнение процедуры. Допустимые синтаксисы:
|
||
Подпрограмма
обработки ошибки обычно включает объект Err, который содержит информацию об
ошибках выполнения. Приведем свойства и методы объекта Err.
Свойства
объекта Err
Number |
Возвращает
код ошибки |
||
Source |
Имя текущего
проекта VBA |
||
Description |
Возвращает строковое
выражение, содержащее текст сообщения об ошибке |
||
HelpFile |
Полное имя (включая
диск и путь) файла справки VBA |
||
HelpContext |
Контекстный
идентификатор файла справки VBA, соответствующий ошибке с кодом, указанным
в свойстве Number |
||
LastDLLError |
Содержит системный
код ошибки для последнего вызова библиотеки динамической компоновки
(DLL) |
||
Методы
объекта Err
Clear |
Очищает все
значения свойств объекта Err. Метод clear используется для явной очистки
значений свойств объекта Err после завершения обработки ошибки. Это
необходимо, например, при отложенной обработке ошибки, которая задается
инструкцией on Error Resume
Next |
||
Raise |
Создает ошибку выполнения. Используется при моделировании ситуаций ошибки. Синтаксис: Raise number, source,
description, helpfile, helpcontext Аргументы:
|
||
На конкретном
примере покажем, как применяется объект Err при создании обработчика ошибок.
В предыдущем разделе в процессе создания диалотового
окна деление и связанной с ним программы, на первый взгляд были предусмотрены
все возможные ошибки. Но это только на первый взгляд. Введем, например, в поле
Знаменатель значение 1е-40. Это число ничем не лучше или не хуже любого другого
числа типа single. Тем не менее, вместо вывода результата произойдет аварийное
прерывание выполнения программы с отображением сообщения об ошибке переполнения.
Усовершенствуем программу с учетом возможности обработки подобной ошибки. В
обработчике ошибок предусмотрим два отклика:
Рис.
12.4. Диалоговое окно с указанием типа ошибки
Private Sub
CommandButtonl_Click()
Dim Числитель, Знаменатель, Результат As Single
'
' Передача управления
на обработчик ошибок,
' помеченный меткой Обработка
'
On Error GoTo Обработка
'
' Проверка,
является ли числитель числом
'
If IsNumeric(TextBoxl.Text)
= False Then MsgBox "Ошибка в числителе",
vblnformation, "Деление" TextBoxl.SetFocus
Exit Sub
End If
' Проверка, является ли знаменатель числом
'
If IsNumeric(TextBox2.Text)
= False Then
MsgBox "Ошибка
в знаменателе",
vblnformation,
"Деление"
TextBox2.SetFocus
Exit Sub
End If
'
' Проверка,
не является ли знаменатель нулем
'
If CDbl(TextBox2.Text)
= 0 Then
MsgBox "Знаменатель
не может быть нулем", vblnformation, "Деление"
TextBox2.SetFocus
Exit Sub
End If
'
Числитель = CDbl(TextBoxl.Text)
Знаменатель = CDbl(TextBox2.Text)
Результат = Числитель / Знаменатель
TextBox3 . Text = CStr (.Результат)
'
' Выход из процедуры в случае успешного нахождения результата
'
Exit Sub
'
' Обработчик ошибок
'
Обработка: Select
Case Err.Number
'
' Обработка ошибки переполнения
'
Case Is = 6
MsgBox "
Произошла ошибка переполнения",
vblnformation, "Деление"
TextBoxl.Text
= 1
TextBox2.Text = 1
Знаменатель = 1
Числитель = 1
Resume
' Обработка любой другой ошибки
'
Case Else
MsgBox "Произошла
ошибка: " & Err.Description &
vblnformation, "Деление"
Exit Sub
End Select
End Sub
В заключение
отметим, что в случае, если разрабатываемое приложение состоит из нескольких
процедур, причем в некоторых из них необходимо создать по обработчику ошибок,
бывает более удобно для сокращения программы и для большей ясности структуры
кода написать отдельную процедуру с обработчиком всех ошибок.
При написании
программ пользователь независимо от его опыта допускает те или иные ошибки.
Кто-то из мудрых совершенно верно подметил, что только тот, кто ничего не делает,
не совершает ошибок. Позвольте перефразировать эту жизненную мудрость для изучаемого
в данной книге предмета следующим образом: в программах не делал ошибок только
тот, кто никогда не писал их. Итак, ошибки — это объективная неизбежность или
реальное воплощение этой неизбежности. Какие же бывают ошибки и как с ними бороться?
Условно ошибки можно поделить на три типа: ошибки компиляции, выполнения и логические
ошибки.
Ошибки компиляции
возникают, если VBA не может интерпретировать введенный код. Например, при некорректном
вводе числа скобок, неправильном имени, неполном вводе инструкции и т. д. Некоторые
из этих ошибок обнаруживаются VBA при завершении набора строки с инструкцией
в редакторе кода и после нажатия клавиши <Enter>. Строка, в которой содержится
ошибка, выделяется красным цветом, и на экране отображается диалоговое окно
с сообщением о возможной причине, вызвавшей ошибку (рис. 12.5).
Рис.
12.5. Сообщение об ошибке компиляции, обнаруженной при вводе инструкции
Рис.
12.6. Сообщение об ошибке компиляции в диалоговом окне Microsoft
Visual Basic
Другие ошибки
компиляции обнаруживаются перед выполнением программы. Отметим, что VBA каждый
раз автоматически компилирует программу при ее запуске на выполнение. В VBA
предусмотрена также возможность компилировать программу без запуска на выполнение
посредством команды Отладка, Компилировать (Debug, Compile). В этом случае
предполагаемое местоположение ошибки выделяется синим цветом и на экране отображается
диалоговое окно Microsoft Visual Basic с сообщением о возможной причине,
вызвавшей ошибку (рис. 12.6).
Ошибки выполнения
возникают после успешной компиляции программы при ее выполнении. Причинами таких
ошибок могут быть, например:
В этих и
подобных случаях на экране отображается диалоговое окно Microsoft Visual
Basic с сообщением о номере ошибки и возможной причине, ее вызвавшей (рис.
12.7).
Рис.
12.7. Сообщение об ошибке выполнения в диалоговом окне Microsoft
Visual Basic
Если в диалоговом
окне Microsoft Visual Basic нажать кнопку Отладка (Debug), то
в строке модуля желтым цветом будет выделена строка, вызвавшая ошибку и по причине
которой выполнение программы было прервано. Кроме того, эта строка будет помечена
стрелочкой. При прерывании выполнения программы VBA переходит в режим прерывания.
Одной из наиболее удобных возможностей режима прерывания являются возможность
узнать текущее значение переменных и свойств. Для этого достаточно расположить
указатель мыши на имени свойства или переменной. Это вызовет появление всплывающей
подсказки с текущим значением переменной или свойства. Из текста кода, изображенного
на рисунке, видно, что значение переменной у равно 0, что и вызвало ошибку прерывания.
Для задания режима вывода всплывающей подсказки с текущими значениями данных
должен быть установлен флажок Подсказки значений данных (Auto Data Tips)
диалогового окна Параметры (Options), вызываемого с помощью команды Сервис,
Параметры (Tools, Options).
Логические
ошибки труднее всего обнаружить и устранить. Эти ошибки не приводят к прерыванию
выполнения программы, т. е. визуально все идет гладко и выглядит так, как будто
программа работает безупречно. Но это только кажущаяся идиллия, т. к. программа
выдает неверные результаты. Локализация логических ошибок связана с тщательным
анализом алгоритма программы с привлечением средств отладки VBA (рис. 12.8).
Рис.
12.8. Редактор кода в режиме прерывания
Простейшим
средством предотвращения случайных ошибок является использование инструкции
Option Explicit. Эта инструкция предписывает явно описывать все переменные,
встречающиеся в программе. Использование инструкции option Explicit позволяет
избежать следующей трудно отслеживаемой ошибки. Предположим, что в программе
используется переменная с именем ссуда, а при наборе имени этой переменной где-то
в программе вместо русской буквы с по ошибке набрана латинская буква с. Визуально,
эти имена ничем не отличаются друг от друга, но воспринимаются программой как
имена разных переменных. Если бы использовалась инструкция option Explicit,
а значит имело место явное описание переменной Ссуда, то компилятор указал бы
на переменную ссуда с латинской буквой с, как на неописанную, и эта, трудно
отслеживаемая ошибка, была бы быстро найдена.
Редактор
VBA позволяет выполнять пошаговую отладку программы. Ее можно выполнить либо
при помощи панели инструментов Отладка (Debug), либо меню Отладка
(Debug), которое включает команды и соответствующие комбинации клавиш (рис.
12.9). Если панель инструментов Отладка (Debug) не отображена на экране,
то ее можно отобразить, выполнив команду Вид, Панели инструментов, Отладка
(View, Toolbars, Debug).
Рис.
12.9. Панель инструментов Отладка и раскрывающееся меню Отладка
Для выполнения
программы в пошаговом режиме используются четыре команды:
Точка останова
устанавливается или снимается с помощью команды Отладка, Точка останова (Debug,
Toggle Breakpoint), либо посредством кнопки Точка останова (Toggle Breakpoint)
панели инструментов Отладка (Debug). На листе модуля точки останова выделяются
полосой кирпичного цвета и кругом того же цвета (рис. 12.10).
В одном проекте
может быть несколько точек останова. Точки останова предназначены для приостановки
выполнения программы. Все инструкции, расположенные выше, между и ниже точек
останова, выполняются в обычном режиме.
Одновременно
снять все точки останова можно, выполнив команду Отладка, Снять все точки
останова (Debug, Clear All Breakpoint).
Вывод
значений свойств и переменных
Одним из
наиболее удобных средств режима отладки является возможность узнать текущее
значение переменных и свойств. Для этого достаточно расположить указатель мыши
на имени свойства или переменной. Это вызовет появление всплывающей подсказки
с текущим значением переменной или свойства. Для установки режима вывода всплывающей
подсказки с текущими значениями данных должен быть установлен флажок Подсказки
значений данных (Auto Data Tips) диалогового окна Параметры (Options),
вызываемого командой Сервис, Параметры (Tools, Options).
Рис.
12.10. Точки останова
Другим способом
отслеживания текущих значений данных является использование диалогового окна
Контрольные значения (Quick Watch), отображаемого на экране с помощью
команды Вид, Окно контрольного значения (View, Quick Watch), либо команды
Отладка, Контрольное значение (Debug, Quick Watch) (рис. 12.11). Диалоговое
окно Контрольные значения (Quick Watch) применяется для одновременного
отображения текущих значений нескольких данных. Команда Отладка, Добавить
контрольное значение (Debug, Add Watch) позволяет добавить новые контрольные
значения в диалоговом окне Контрольные значения (Quick Watch).
Удаление
контрольного значения из диалогового окна производится посредством его выделения
и нажатия клавиши <Delete>.
Существует
также программный способ вывода значений свойств и переменных в диалоговом окне
Контрольные значения (Quick Watch) при помощи метода Print объекта Debug.
Ниже приведен пример программного способа вывода значения переменной:
ПроверяемаяПеременная
= "Вывод текста в окно отладки." Debug.Print ПроверяемаяПеременная
Окно Локальные
переменные (Locals Window), отображаемое на экране командой Вид, Окно
локальных переменных (View, Locals Window), выводит значения всех переменных
текущей процедуры, а не только специально выбранных, как это происходит в окне
Контрольные значения (Quick Watch).
Внешний же
вид и структура обоих окон, Локальные переменные (Locals Window) и Контрольные
значения (Quick Watch), одни и те же.
Рис.
12.11. Диалоговое окно Контрольные значения
Окно Проверка
(Immediate Window), отображаемое на экране командой Вид, Окно отладки
(View, Immediate Window), предоставляет пользователю возможность:
s=0: For i=1 to 5: s=s+i^2:
Next i: MsgBox s
?х
х = 15