VBA в MS Office 2007

Отладка приложений, обработка ошибок

Показывать лекцию целиком

8.1. Ошибки при создании программы

Можно выделить два типа ошибок, с которыми сталкивается программист. Во-первых - это ошибки, которые сопровождают создание программ, а во вторых - ошибки времени выполнения.

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

Приведем несколько примеров синтаксических ошибок.

  • Неправильное ключевое слово. Такая ошибка сразу будет отмечена редактором. Например, если в конструкции цикла For-Next сделать ошибку в ключевом слове For - вы увидите сообщение об ошибке как только попытаетесь перейти к написанию следующей, после начала цикла, строки.
  • Ошибка при объявлении переменной. Если вы забудете указать ключевое слово Dim при объявлении переменной - редактор выдаст сообщение об ошибке. Однако, сделает он это лишь при попытке запуска программы.
  • А вот - логические ошибки.

  • Неправильное использование операторов. Например, вместо знака обычного деления вы случайно использовали знак целочисленного деления.
  • Расчет какого-либо показателя по неправильной формуле. Например, если неточно расставить скобки в каком-либо выражении - это приведет к ошибке, хотя внешне все может выглядеть правильным.
  • Неправильное использование функций - как встроенных, так и пользовательских. Например, используя функцию Str для получения строкового представления числа, вы не учли, что для положительных чисел эта функция добавляет в начало строки пробел. Далее вы попытались узнать первую цифру числа, вырезав первый символ полученной строки. Естественно, никакой цифры в этом случае не получится - лишь знак пробела для положительных или "минус" для отрицательных чисел.
  • Неправильное использование переменных. Например, вы используете два вложенных цикла для обработки двумерного массива. Одна из цикловых переменных имеет имя i, вторая - j. Они довольно сильно похожи внешне, их можно случайно перепутать при указании индексов массива. К тому же, обрабатывая массив в цикле довольно легко перепутать место каждой из переменных при указании индекса массива. Использование понятных имен переменных (например - my_Age или num_Vozrast для хранения возраста и т.д.) позволяет эффективно бороться с такими ошибками.
  • Случайное использование "новых" переменных. Например, вы предложили пользователю ввести некое значение и записали его в переменную num_Inp, а использовав эту переменную в выражении, напечатали не num_Inp, а num_Ihp. Внешне они похожи, но, присмотревшись, вы можете обнаружить, что имена разные. Еще сложней искать ошибки в латинских именах переменных, в которые "вкрались" русские буквы. Разницу между my_Name и my_Namе вы не увидите, но это - разные переменные - в конце второй вместо латинской e использована русская е. Эффективно бороться с такими ошибками можно, если задать в редакторе опцию обязательного объявления переменных ( Option Explicit ). При появлении необъявленной переменной редактор даст знать об этом.
  • Неправильное использование оператора сравнения. Например, это может быть оператор, который сравнивает некие величины не так, как вы предполагали - вместо знака < вы случайно использовали > или, редактируя сравнение (скажем, скопировав похожий оператор сравнения из другого места программы для ускорения работы), поменяли местами сравниваемые переменные или выражения, не поменяв знака и т.д.
  • Ошибки логики можно найти лишь тщательно проверив и протестировав программу. Чтобы облегчить работу с кодом программы, рекомендуется снабжать ее комментариями. Вовсе необязательно комментировать каждую строчку, достаточно выделить крупные функциональные блоки.

    8.2. Ошибки при выполнении программы

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

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

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

    8.3. Тестирование программ и поиск ошибок

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

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

    Если обычные проверки закончились успешно - можно считать, что ваша программа корректно работает, не имеет синтаксических и логических ошибок.

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

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

  • Если программа запрашивает число - введите какое-нибудь слово, очень большое число, ноль, отрицательное число, оставьте поле ввода пустым, введите дробное число. Если вы не предусмотрели никаких специальных мер по обработке ошибок, при выполнении подобных операций вы почти гарантированно встретитесь с проблемами.
  • Попытайтесь запустить программу, открыв несколько окон с документами.
  • Попытайтесь прервать работу программы, а потом снова возобновить ее. Если в вашей программе есть участки, в течение выполнения которых нельзя допускать прерывания работы программы пользователем - вам следует подумать о том, чтобы запретить прерывание работы программы на этих участках.
  • Попытайтесь использовать вашу программу в более старой версии Microsoft Office, в нерусифицированной версии. Обратите внимание, например, на различия расширений имен файлов в Office 2007 и более старых версиях, на различия в объектных моделях. В общем случае программы из более старых версий Office будут работать в Office 2007, однако если они используют какие-то специфические особенности Office - такие программы нуждаются в проверке и обновлении. В то же время, макросы для Office 2007, использующие новые объектные модели, могут не работать или работать неправильно в старых версиях.
  • Попытайтесь поработать с вашей программой на чужом ПК. Вполне возможно, что при таком эксперименте вы столкнетесь с ошибкой. Например, вы программно работаете с файлами на вашем рабочем ПК - если эти файлы отсутствуют на ПК другого пользователя или находятся в других директориях, или тех директорий, которые нужны вашей программе, нет на ПК другого пользователя - вы столкнетесь с ошибкой.
  • Во время работы программы сделайте что-нибудь необычное. Как правило, от пользователей можно ожидать любых странных на первый взгляд действий. Если вы тестируете программу для MS Word, которая правит текст или занимается автоматическим созданием текста, попробуйте во время ее работы переключаться между документами, читать документ, вносить в него правки, выделять произвольные участки текста. То же самое касается MS Excel - во время работы программы попробуйте переключаться между открытыми книгами, между листами, выделять ячейки, попробуйте запустить программу, делая различные листы активными, открыв несколько книг. Результаты такого тестирования могут быть совершенно непредсказуемыми. Проанализировав их, вы можете прити к выводу, что, например, на время выполнения программы нужно скрывать или блокировать документ, пользоваться альтернативными методами работы с документом. Яркий пример - объект Selection в MS Word, который чувствителен к смене выделения в процессе работы, и объект Range, который может работать совсем без создания выделения в тексте.
  • Если ваша программа использует файлы, находящиеся в локальной сети, отключите сеть во время работы программы. Проверьте ее реакцию. То же самое можно сделать, если ваша программа работает с принтером - проверьте ее реакцию на выключенный принтер, на принтер, в котором нет бумаги.
  • Наконец, представьте, что вы - пользователь программы и просто поработайте с ней. А еще лучше - попросите потенциального пользователя немного "пообщаться" с вашей программой. Этот способ позволяет протестировать программу в условиях, максимально приближенных к реальным.
  • Возможно, вам покажется, что такая проверка не нужна вашей программе. Если вы пишете небольшой макрос для собственного использования, скорее всего, так оно и есть. Но стоит вашему проекту хоть немного вырасти, методы поиска и устранения ошибок могут оказаться очень кстати.

    Если вы встретитесь с ошибками и затрудняетесь определить, где именно они происходят - попробуйте выполнить программу в пошаговом режиме, используйте встроенные в редактор VBA средства для отладки программ.

    8.4. Отладка программ в редакторе VBA

    Основной метод отладки - это пошаговое исполнение программы с использованием точек останова (breakpoint).

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

    (рис 8.1) Точка останова в программе

    Следует понимать, что строка, подсвеченная при остановке программы еще не выполнялась - редактор указывает на нее, как бы говоря "Эта строка будет выполнена следующей".

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

    Установить точку останова в строку можно, выделив строку и выбрав команду Debug o Toggle Breakpoint (Отладка o Установить точку останова). Для удаления всех точек останова из программы можно воспользоваться командой Debug o Clear All Breakpoints (Отладка o Очистить точки останова).

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

    Так же режим отладки можно включить, нажав во время работы программы комбинацию клавиш Ctr + Pause Break и нажав в появившемся окне кнопку Debug. Текущая строка будет выделена желтым цветом, напротив нее будет установлена желтая стрелочка. Однако такой способ обычно не позволяет точно "попасть" в то место программы, где находится предполагаемая ошибка (рис. 8.2.).

    (рис 8.2) Программа, остановленная во время выполнения

    Чтобы запущенная программа останавливалась на каждой строке, можно запустить ее в режиме Step Into командой Debug o Step Into (Отладка o Пошаговое исполнение). Того же эффекта можно достичь, нажав клавишу F8 на клавиатуре.

    Когда программа остановлена, вы можете выполнить следующие действия

  • Просмотреть значения переменных, наведя на них указатель мыши. Например, на рис 8.3(рис 8.3) Значение переменной во всплывающем окне
  • Продолжить выполнение программы в режиме Step Into - выбрав соответствующую команду меню или нажав клавишу F8.
  • Отредактировать программу.
  • Продолжить исполнение программы в обычном режиме командой Run o Sub/User Form (Запустить o Процедуру/Форму), нажатием клавиши F5 или соответствующей кнопкой на панели инструментов
  • Остановить выполнение программы командой Run o Reset (Запустить o Перезагрузка) или кнопкой на панели инструментов
  • Воспользоваться другими средствами отладки - окнами Immediate, Locals, Watch.
  • Помимо режима Step Into существуют следующие режимы отладки, доступные в меню Debug.

  • Step Over (Перейти на следующую строку). Эта команда полезна при отладке программы, содержащей вызовы уже отлаженных процедур. В режиме Step Over отладчик не входит в процедуру, выполняя ее без отладки, после чего переходит на следующую строку. Например, вы выполняете программу в режиме Step Into и при очередной остановке видите, что подсвеченная строка содержит вызов процедуры, которую отлаживать не нужно. Вы выбираете команду Step Over, процедура выполняется без остановок на каждой ее строке, после чего следующая остановка происходит на строке вашей программы, которая идет за вызовом процедуры.
  • Step Out (Выполнить процедуру) - эта команда позволяет выполнить текущую процедуру (например, вызванную из кода основной программы при обычной отладке ) без остановки в каждой строке. Следующая остановка будет сделана на строке, которая следует за вызовом процедуры в основном тексте программы.
  • Run To Cursor (Выполнить до курсора) - выполняет программу до позиции, на которой установлен курсор. Аналогично установке одиночной точки останова.
  • Помимо точек останова существуют и другие средства отладки. Они полезны при проверке значений переменных (ведь если переменных достаточно много - проверка их значений в коде программы может превратиться в утомительное и непродуктивное занятие), свойств объектов, которые могут вызвать ошибки и в других случаях.

    8.5. Immediate

    08-01-Работа с Immediate.docm - пример к п. 8.5.

    Окно Immediate (Немедленное выполнение) представляет собой нечто вроде "филиала" редактора кода. Чтобы отобразить его, можно воспользоваться командой View o Immediate Window (Вид o Окно непосредственных изменений). С помощью Immediate можно мгновенно выполнять различные команды, вызывать процедуры и функции - как пользовательские, так и встроенные. Так же в него можно организовать вывод различных данных в ходе выполнения программы. Давайте рассмотрим основные возможности этого окна.

    Для отправки в окно Immediate информации из выполняющейся программы нужно воспользоваться такой конструкцией:

    Debug.Print

    Метод Print объекта Debug организует вывод в Immediate. После того, как выполнение программы завершится, вы сможете проанализировать выведенные данные.

    Например, добавим в VBA-проект форму, назовем ее frm_Test, на форму добавим кнопку - cmd_Test. Добавим в код обработчика нажатия кнопки такой код (листинг 8.1.):

    Dim TwoDim(4, 5)
        Dim OneDim(30)
        Dim num_Transfer
        For i = 0 To 4
            For j = 0 To 5
                TwoDim(i, j) = Int(Rnd * 100)
            Next j
        Next i
        For i = 0 To 4
            Debug.Print ("i=" + Str(i))
            For j = 0 To 5
                num_Transfer = num_Transfer + 1
                OneDim(num_Transfer) = TwoDim(i, j)
                Debug.Print ("j=" + Str(j))
                Debug.Print ("OneDim" + Str(num_Transfer) + _
                    " =" + Str(OneDim(num_Transfer)))
                Debug.Print ("TwoDim" + Str(i) + Str(j) + _
                    " =" + Str(TwoDim(i, j)))
            Next j
        Next i

    Здесь мы объявили два массива - один двумерный ( TwoDim ), второй одномерный ( OneDim ). Заполнили TwoDim случайными числами, после чего организовали перенос данных из него в OneDim. В циклическую конструкцию, которая осуществляет перенос данных, мы вставили несколько вызовов Debug.Print. Они позволили нам получить полную картину работы этого блока в окне Immediate (рис. 8.4.).

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

    (рис 8.4) Окно Immediate после вывода значений

    На рис. 8.5. вы можете видеть окно Immediate, которое содержит несколько команд. Эти команды мы вводили в режиме останова программы из листинга 8.1.

    (рис 8.5) Работа с Immediate в режиме останова

    Команда ?. Если вы программировали на обычном Basic'e, этот значок должен быть вам хорошо знаком.

    Командой ? 25*14-4+11^7 мы вычисляем введенное выражение.

    Командой ? TwoDim(1, 1) мы выводим число, хранящееся в TwoDim(1, 1). В нашем случае это 76. После этого мы записываем команду TwoDim(1, 1)=11, тем самым присваивая элементу массива новое значение, и выводим это значение снова.

    Теперь рассмотрим возможности по запуску процедур и функций. Создадим в коде формы простую процедуру с именем hello, которая выводит окно сообщения с текстом "Привет". Чтобы вызывать эту процедуру, не выполняя программу, достаточно ввести в окно Immediate такой код: frm_Test.hello. Вот, как выглядит окно после ввода команды (рис. 8.6.).

    (рис 8.6) Окно кода с пользовательской процедурой и Immediate после ввода команды

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

    Как видите, Immediate - это очень простое, но мощное средство отладки. Однако, его неудобно использовать для быстрого просмотра множества свойств элементов управления, значений многих переменных и т.д. В подобных случаях нам может помочь окно Locals.

    8.6. Locals

    Окно Locals (Локальные переменные) позволяет просматривать и изменять свойства всех объектов приложения и значения переменных. Посмотрите, как выглядит окно Locals при открытии его в режиме останова программы, с которой мы работали в п. 8.5. (рис. 8.7.)

    (рис 8.7) Окно Locals открытое при останове программы

    Как видите, здесь есть объект Me, который представляет собой форму, массивы, объявленные в коде, переменные. Для посмотра или редактирования свойств объектов и содержимого массивов, достаточно развернуть их описания, нажав на знак + перед их наименованиями. Для редактирования значения нужно просто выделить его и ввести вместо старого новое. Учтите, что строковые значения нужно вводить в кавычках - так же, как и в программном коде.

    8.7. Watches

    Окно Watches (Контролируемые выражения) предназначено для контроля за отдельными выражениями, значениями переменных и т.д.

    Чтобы добавить такое выражение, щелкните мышью по нему и выберите команду меню Add Watch (Контролировать). Появится окно Add Watch (рис. 8.8.).

    (рис 8.8) Окно Add Watch

    Здесь, в поле Expression (Выражение) нужно ввести контролируемое выражение. В нашем случае это имя переменной.

    В группе параметров Context (Контекст) следует выбрать область контроля. Чем эта область меньше (в нашем случае - это процедура cmd_Test_Click, которая хранится в модуле формы frm_Test ) - тем лучше.

    В группе параметров Watch type (Тип наблюдения) нужно выбрать тип наблюдения за выражением.

    Watch Expression (Наблюдать за выражением) - программа просто выводит в окно Watch значение наблюдаемого выражения, ничего не предпринимая. Если вы перейдете в режим отладки, то сможете просмотреть это выражение.

    Break When Value Is True (Прервать работу программы если выражение выполняется) - при выполнении определенного в окне Add Watch условия программа остановится и перейдет в режим отладки. Например, если мы напишем в поле Expression выражение num_Transfer > 100 и активируем данный пункт, то работа программы будет остановлена лишь тогда, когда значение переменной num_Transfer превысит 100.

    Break When Value Changes (Прервать работу программы при изменении значения) - прерывает выполнение программы если значение контролируемой переменной или выражения изменилось. Если мы сделаем эту опцию активной, при каждом изменении переменной программа будет переходить в режим отладки. Установим эту опцию и запустим программу. При первом же изменении переменной num_Transfer мы попадем в режим отладки (рис. 8.9.).

    (рис 8.9) Работа с окном Watches в режиме отладки

    Как видите, в окне Watches отображены следующие данные:

  • Expression - контролируемое выражение.
  • Value - значение выражения.
  • Type - тип данных.
  • Context - контекст (область контроля).
  • При необходимости вы можете отредактировать контролируемое выражение - для этого щелкните правой кнопкой мыши по строке выражения в окне Watches и выберите пункт Edit Watch. Для удаления выражения выберите в том же меню пункт Delete Watch, для добавления нового - Add Watch.

    8.8. Проверка данных, вводимых пользователем

    08-02-Проверка введенных даных.docm - пример к п. 8.8.

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

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

  • должен быть числом
  • ограничим введенное значение диапазоном значений для типа данных Byte - то есть введенное значение не может быть больше 256 и меньше 0.
  • скорее всего, он не превысит 100 лет, но, учитывая то, что нашей программой могут пользоваться и долгожители (если верить статистике, некоторые доживают и до 150) - переспросим пользователя, уверен ли он в том, что ввел именно это значение, и, если он согласится, примем его.
  • Так же мы ограничим количество возможных попыток для ввода возраста тремя.

    Добавим в документ Microsoft Word две кнопки. Одну из них назовем cmd_AgeInput и подпишем как Введите ваш возраст, вторую назовем cmd_InpAge_Easy и подпишем как Простой ввод возраста.

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

    В листинге 8.2. приведена функция, которая проверяет, соответствует ли введенное число нашим требованиям.

    Public Function IsGoodNum(var_Inp) As Boolean
    'Функция возвращает True, если введенное
    'число подходит, иначе - False
        If IsNumeric(var_Inp) = True _
            And Val(var_Inp) > 0 _
            And Val(var_Inp) < 255 _
        Then
            IsGoodNum = True
        Else
            IsGoodNum = False
        End If
    End Function

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

    Public Function IsMore100(var_Inp) As Boolean
    'Функция возвращает True, если введенное число
    'более 100
        If Val(var_Inp) > 100 Then
            IsMore100 = True
        Else
            IsMore100 = False
        End If
    End Function

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

    Public Function NotNumber(var_Inp) As String
    'Определяет, что именно ввел пользователь
    'вместо ожидаемого числа и возвращает
    'строку для вывода сообщения об ошибке
        'Если не выполнится ни одно из условий
        'программа выдаст общее сообщение об
        'ошибке ввода данных
        NotNumber = "Ошибка ввода данных"
        'Если ей удастся определить тип ошибки
        'будет выведено более точное сообщение
        If var_Inp = "" Then
            NotNumber = "Вы забыли ввести возраст"
        End If
        If Val(var_Inp) < 0 Then
            NotNumber = "Вы ввели отрицательное число"
        End If
        If IsNumeric(var_Inp) = False Then
            NotNumber = "Вы ввели не число"
        End If
        If Val(var_Inp) > 255 Then
            NotNumber = "Вы ввели слишком большое число"
        End If
    End Function

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

    Private Sub cmd_AgeInput_Click()
        'Переменная для ввода пользователм
        Dim str_Inp
        'Сюда запишем возраст после проверки
        Dim num_Age As Byte
        'А здесь будем хранить ответ пользователя на
        'Вопрос о том, уверен ли он в том, что ему
        'Больше 100 лет
        Dim IsYouMore100 As Variant
        'В этой переменной будем хранить количество
        'Неправильных попыток ввода
        'Если оно превысит 3 - выходим из программы
        Dim num_InpTry As Byte
        'Эта переменная нужна для того, чтобы
        'определить, окончен ли опрос пользователя
        Dim bool_Flag As Boolean
        bool_Flag = False
        'Этот цикл будет длиться до тех пор,
        'пока пользователь не введет верное значение
        'или пока не совершит максимальное число
        'попыток
        Do
        str_Inp = InputBox("Введите ваш возраст")
            If num_InpTry > 3 Then
                MsgBox "Вы совершили более 3 ошибок"
                Exit Sub
            End If
            num_InpTry = num_InpTry + 1
            'Если введенное число подходит
            If IsGoodNum(str_Inp) = True Then
                'Проверяем - не больше ли оно 100
                If IsMore100(str_Inp) = False Then
                'Если не больше - просто устанавливаем
                'флаг выхода в True
                'и записываем возраст в переменную num_Age
                    bool_Flag = True
                    num_Age = CByte(str_Inp)
                Else
                'Если возраст больше 100 - переспросим
                    IsYouMore100 = _
                    MsgBox("Вам точно больше 100?", vbYesNo)
                    'Если пользователь не ошибся,
                    'Установим флаг выхода
                    'и запишем возраст в num_Age
                    If IsYouMore100 = vbYes Then
                        bool_Flag = True
                        num_Age = CByte(str_Inp)
                    End If
               End If
            Else
            'Если пользователь изначально ввел
            'неподходяще число,
            'сгенерируем с помощью функции NotNumber
            'сообщение об ошибке и выведем его
                MsgBox (NotNumber(str_Inp))
            End If
        Loop Until bool_Flag = True
        MsgBox ("Ваш возраст: " + Str(num_Age))
    End Sub

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

    Private Sub cmd_InpAge_Easy_Click()
        Dim str_Inp
        Dim num_Age As Byte
        'Цикл продолжается до тех пор,
        'пока введенное значение не станет
        'числом, находящимся в диапазоне от
        '0 (не включая 0) до 100
        Do
            str_Inp = _
            InputBox("Введите ваш возраст")
        Loop Until _
        IsNumeric(str_Inp) = True _
        And Val(str_Inp) <= 100 _
        And Val(str_Inp) > 0
        'Конвертируем введенное число в
        'тип данных Byte
        num_Age = CByte(Val(str_Inp))
        MsgBox ("Ваш возраст: " + Str(num_Age))
    End Sub

    Как видите, здесь мы ограничились тройной проверкой на выходе из цикла с постусловием. А именно, проверили, является ли введенное значение числом, и попадает ли оно в диапазон 0-100, исключая 0.

    8.9. Работа с ошибками времени выполнения

    08-03-Ошибки времени выполнения.docm - пример к п. 8.9.

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

    В этой ситуации нам поможет перехват и обработка ошибок времени выполнения.

    Давайте рассмотрим ситуацию, которая гарантированно вызовет ошибку - деление на 0. Добавим в документ Microsoft Word кнопку с именем cmd_SimpleInput, подпишем ее как Отсутствие перехвата ошибок.

    Напишем простую программу (поместив ее в обработчик события Click кнопки cmd_SimpleInput ), которая запрашивает у пользователя два числа, делит одно на другое и выводит результат деления. Следующий код (листинг 8.7.) реализует эту функциональность:

    Dim num_A
        Dim num_B
        Dim num_C
        num_A = InputBox("Введите число A")
        num_B = InputBox("Введите число B")
        num_C = num_A / num_B
        MsgBox ("А, деленое на В равняется: " + Str(num_C))

    Чтобы организовать обработку ошибок в программе нужно расположить перед потенциально опасным участком (в нашем случае - перед строкой, где происходит деление) оператор такого вида:

    On Error GoTo Идентификатор_обработчика

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

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

    После того, как опасный участок программы пройден, обработку ошибок желательно отключить - это можно сделать такой командой:

    On Error GoTo 0

    Так же при обработке ошибок можно пользоваться командой

    On Error Resume Next

    Эта команда подавляет вывод сообщения об ошибке, просто передавая управление в следующую, после ошибочной, строку.

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

    Метод Clear очищает информацию об ошибках в объекте Err.

    Метод Raise позволяет сгенерировать ошибку, указав в качестве параметра ее номер и некоторые другие, необязательные, данные. Эта возможность полезна при тестировании программ, оснащенных обработчиками ошибок, чтобы разработчик мог произвольно генерировать те или иные ошибки.

    Номера ошибок находятся в диапазоне 0 - 65535. В частности, номера 0 - 512 зарезервированы за системными ошибками, 513 - 65535 - за ошибками, коды которых пользователь может назначать самостоятельно.

    Например, для вызова ошибки несоответствия типов переменных (она возникает при попытке записать в числовую переменную строковое значение и в других случаях), которая имеет код 13, достаточно выполнить такую команду:

    Err.Raise (13)

    Свойство Number содержит номер ошибки. Обычно этот номер используют для выбора действия в обработчике ошибок.

    Свойство Description содержит текстовое описание ошибки

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

  • если число B (делитель) будет равно 0 - это ошибка №11 - "Деление на 0"
  • если одно из введенных чисел (или оба) на самом деле - не числа. Например, пользователь случайно нажал OK, не введя число в поле ввода. Это - ошибка №13 - "Несоответствие типов"
  • если оба числа равны нулю - такая ситуация вызовет ошибку №6 - "Переполнение"
  • Нужно учитывать так же, что чисто теоретически возможно возникновение ошибки, для которой мы не предусмотрели определенный раздел обработчика.

    В результате этих рассуждений у нас получился такой код (листинг 8.8.), который мы разместим в обработчике события Click кнопки cmd_ErrHandler, подписанной как Обработка ошибок и размещенной в том же документе, что и вышеописанная кнопка

    Dim num_A
        Dim num_B
        Dim num_C
        num_A = InputBox("Введите число A")
        num_B = InputBox("Введите число B")
        On Error GoTo ErrHandler
        'С этого момента начинаем наблюдение
        'за ошибками
        num_C = num_A / num_B
        On Error GoTo 0
        'Прекращаем наблюдение за ошибками
        MsgBox ("А, деленое на В равняется: " + _
        Str(num_C))
        Exit Sub
    ErrHandler: 'Начало обработчика ошибок
        MsgBox ("Вызвана ошибка №" + _
        Str(Err.Number) + " " + _
        Err.Description)
        Select Case Err.Number
        Case 6:
        'Overflow - переполнение
        'обе переменные равны 0
            MsgBox ("Вы ввели A и B = 0")
            num_A = InputBox("Введите число A")
            num_B = InputBox("Введите число B")
        Case 11:
        'Деление числа на 0
            MsgBox ("Вы ввели B = 0")
            num_B = InputBox("Введите число B")
        Case 13:
        'Type mismatch - несоответствие типов
        'одно из чисел или оба - не числа
            MsgBox ("Одно из чисел или оба - не числа")
            num_A = InputBox("Введите число A")
            num_B = InputBox("Введите число B")
        Case Else:
        'на случай возникновения
        'непредусмотренной ошибки
            num_A = InputBox("Введите число A")
            num_B = InputBox("Введите число B")
        End Select
        Resume
    End Sub

    Обратите внимание на то, что перед меткой строки, с которой начинается обработчик мы поставили команду Exit Sub. Это сделано для того, чтобы программа, дойдя до последней строки своей основной части, не начала бы выполнять обработчик ошибок.

    Перед опасной операцией деления мы поставили команду On Error GoTo ErrHandler - если при делении произойдет ошибка, будет вызван обработчик. Необязательно устанавливать эту команду строго перед опасной процедурой - мы можем поставить ее в любом месте программы до нее. После того, как опасный участок пройден - следует команда On Error GoTo 0 - она отключает слежение за ошибками.

    Внутри обработчика мы, прежде всего, выводим окно сообщения, где указываем номер ошибки и описание - и то и другое получено с помощью соответствующих свойств объекта Err. Далее, мы используем номер, хранящийся в свойстве Number объекта Err, чтобы произвести различные действия в зависимости от ошибки. В частности, наша программа может определять три вида ошибок ввода, сообщать об этом пользователю и запрашивать ввод переменных. Для обработки других ошибок, которые могут возникнуть, создана ветвь Case Else.

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

    8.10. Выводы

    В этой лекции мы рассмотрели вопросы отладки программ и работы с ошибками. Теперь вы владеете основными приемами VBA, общими для программирования в любых приложениях Office. В следующих лекциях мы займемся программированием для MS Word и MS Excel.

    Вернуться к учебному плану