Для зарегистрированных пользователей |
|
Подвижный VBA'стик в кислотной ActiveX-среде
Арсений Чеботарев
Арсений Чеботарев, "Комиздат"
Обычно книги и статьи по VBA посвящены тому, что можно сделать на VBA, но для чего он совсем не предназначен. Так, например, у меня есть книга (на 800 страниц), посвященная, в основном, таким вещам, как системные вызовы и конструирование древовидных структур вручную. Это как раз то, чего нужно бы избегать,- если вы не пишете трояны, конечно.
В этом смысле данная статья - полная противоположность книгам такого рода, то есть мы займемся именно тем, для чего VBA предназначен: созданием и "оживлением" COM-объектов. Полученная программка будет полезна не только в качестве примера, но и практически - то есть ее можно использовать и даже, если повезет, продать.
Use Case с точки зрения программиста
Проиллюстрированная далее программа показывает, как VBA получает доступ, создает и управляет ActiveX-элементами за пределами иерархии классов Office. Создавать элементы управления не имеет особого смысла, если не обрабатывать специфические для них события. Поскольку ActiveX-элементы у нас будут создаваться динамически и их количество не будет даже предварительно известно, то и обработчики будут генерироваться динамически. Да и поведение наших "кнопок", хотя и будет выбираться из нескольких вариантов, но все-таки должно определяться в последний момент… Короче, если вы пробовали генерировать программы, начиная от LISP и prolog и заканчивая визардами в VS, то вы представляете, о чем речь.
Use Case с точки зрения пользователя и заказчика
Типичный заказчик: вуз, школа, курсы - любое учебное заведение, которое желает экзаменовать своих учеников методом тестирования. Для использования понадобится, естественно, составить собственный банк вопросов и ответов в предметной области. Процесс это кропотливый и трудоемкий, но здесь мы совершенно его не рассматриваем.
Прежде чем начать работу, включите выполнение макросов: в Сервис> Макросы> Безопасность поставьте переключатель в положение Низкая.
Не страшно ли включать макросы в Office? Нет, не страшно. Проблема распадается на два случая. Если у вас стоит антивирус - то трояны в макросах "отдыхают" по-любому. Если же антивируса у вас нет - то и макровирусов вам тоже незачем бояться, у вас уже и так, наверное, полный диск другой живности.
Ко всему прочему, макровирусы не отличаются жестокостью, так что вы можете установить антивирус, но не включать его в режиме постоянного сканирования, а всего лишь раз в день запускать на сканирование. Дополнительно можно рекомендовать антивирусную защиту почты - пользуйтесь почтовым сервером, который применяет антивирусные сканеры, поскольку этот путь инфицирования сейчас наиболее распространен. (Из личного опыта: за 15 лет работы за клавиатурой я не потерял ни единого бита из-за вирусов - а всё только по причине собственной рассеянности и пьянства. Так что для меня вирусы - скорее легенда, чем реальная угроза.)
И последнее: никто не отменял резервного копирования и других методов backup'а, таких как хранение эталонной системы в формате Norton Ghost. Благо, CD-RW и прочие носители сейчас дешевле грибов.
Постановка задачи
Есть сборник вопросов в некоторой предметной области (в терминах экзаменаторов - банк данных). Вопросы разделены на несколько тем или групп - уровень деления 1, то есть у тем нет подтем. Все вопросы имеют свой "вес" от 50 до 100, вес определяет важность вопроса: 100 соответствует наиболее важным, а 50 - самым "проходным" вопросам. Каждому вопросу соответствует несколько (обычно 3-5) ответов. Каждый ответ тоже имеет свой "вес" от 100 до 0: 100 соответствует абсолютно правильным и полным ответам, 80 - частично правильным, 50 - не лишенным элементов истины и 0 - полностью неверным. Предполагается, что для каждого вопроса существует один и только один абсолютно правильный ответ. Для проведения экзамена из всего банка случайным образом выбирается определенное количество вопросов, их порядок меняется произвольным образом. При этом можно выбрать одну, несколько или все темы. Ответы также меняются местами. Предварительно выбранные вопросы кладутся в "конверт", который экзаменуемый вскрывает во время экзамена.
После ответов подсчитывается общий балл. Происходит это следующим образом: складываются все веса всех вопросов как максимальное число баллов, которое возможно набрать. Реально набранное число баллов определяется суммой Sum (Vx*Mx) - то есть веса вопросов на вес полученного ответа. Успеваемость экзамена вычисляется как процент набранных баллов от максимально возможных.
Дополнительные условия
Для того чтобы программа была полезной, она должна работать в условиях реального мира. В данном случае заказчик поставил следующие требования:
- программа должна работать на любых компьютерах, на которых запускается Windows, в том числе без CD и сети;
- программа должна работать без инсталляции какого-либо ПО, системы управления базами данных, сети, интернета и так далее. Единственная предпосылка - наличие на компьютере Windows и Word. Это довольно строгое ограничение - но, тем не менее, жизненное: инсталляция (или проверка наличия) того же BDE или MS Access на 100-200 компьютерах может значительно откорректировать бюджет любого начинания;
- вопросы должны изменяться самым простым и непосредственным образом, для этой операции не нужно проходить никакого инструктажа или обучения;
- подготовленные "конверты" с вопросами должны иметь минимальную степень защиты от модификации, так чтобы экзаменуемый в присутствии экзаменатора не мог нечаянно или с минимальными усилиями изменить результаты в свою пользу. Безусловно, это не касается "людей от компьютера", которые в комфортных условиях и при наличии времени могут сломать и видоизменить все что угодно - подразумевается, что экзаменуемый находится в системе тестирования и не занимается хаком нашей программы.
Итак, отталкиваясь от всего перечисленного, начнем строить нашу тестовую систему. Первое, что приходится сделать, это выбрать Word как в качестве источника данных, так и в качестве среды выполнения. Такое решение - самое естественное, поскольку ни на какой другой рантайм мы не можем рассчитывать. Первой идеей было использовать текстовый режим и gcc+couses. Но редактирование текстовых файлов в dos-кодировке - это задача, с которой справится не каждый современный пользователь, и, как говорят, "текстовый режим выглядит не современно".
Первое последствие из выбора Word в качестве инструмента: наша база данных будет представлена обычным Word-документом определенного формата. В силу некоторых исторических причин формат текста был следующим:
&Вопросы для системного администратора
"100" Чем нужно зажимать сетевой джек: "100" Специальным инструментом. "50" Плоскогубцами. "0" Зубами. "0" А зачем его вообще зажимать?
"80" Что вы делали на Новый Год: "0" Гулял с друзьями. "100" Переставлял Линукс.
"100" Как у вас проложен сетевой кабель: "50" В коробе. "0" По полу. "100" Мы давно перешли на WaveLAN.
То есть первая строка со значком & обозначает начало темы, потом идут группы строк, первая из которых - вопрос, а остальные - ответы. Строки предваряются весовыми коэффициентами: "важности" - для вопросов, и "степени верности" - для ответов. Согласитесь, что ввести и модифицировать такой файл может любой пользователь. Для представления банка данных создадим три тривиальных класса: ' class BLine
Public Weight As Integer
Public Value As String
Public ff As InlineShape
Public loc As Range
Public Sub Parse (s As String)
p1 = InStr (s, """")
p2 = InStr (p1 + 1, s, """")
If (p1 > 0) And (p2 > p1) Then
Weight = Val (Mid (s, p1 + 1, p2 - 1))
Value = Trim (Mid (s, p2 + 1))
Else
Weight = 0
Value = s
End If
End Sub
' class Question
Public Question As New BLine
Public Answers As New Collection 'Of BLine (s)
' class Theme
Public Title As String
Public Questions As New Collection 'Of Question (s)
Public Selected As Boolean
Как видите, вопрос - это собственно вопрос и коллекция ответов, а тема - это название темы и коллекция вопросов. Это похоже на представление списков в LISP - голова и хвост. Самый главный кирпич всей иерархии, класс BLline, включает в себя строку и ее вес, а также дополнительные поля, смысл которых прояснится позже. Тривиальный метод Perse принимает строку и преобразует ее в поля объекта - немудреный суржик перегрузки конструктора в C++.
Перед тем как собственно выбирать и тасовать вопросы и ответы, построим древовидную структуру, несколько напоминающую DOM-представление,- для этого вызывается специальная функция. Предполагается, что некто, ответственный за составление вопросов, активно читает и исправляет их - и, когда наступает компромисс между совестью и усталостью, формирует нужное количество именных "конвертов" с вопросами для каждого студента.
Есть четыре возможности запустить макрос: по нажатию горячей клавиши, по нажатию кнопки на панели, по системному событию и явно через меню Макросы. Поскольку этот вопрос нас пока не занимает, то пусть наш процесс запускается по Ctrl+K - кнопки на панелях инструментов имеют свойство теряться, а сами панели - быть закрытыми шаловливыми конечностями пользователей.
С высоты птичьего полета выполняем такие вот действия:
- инициализируем используемые структуры и получаем ссылки на необходимые системные (в смысле, Word) объекты;
- строим иерархическое дерево Test - Theme (s) - Question (s) - Bline + Bline (s);
- выводим список тем и количество вопросов в каждой - для выбора вопросов только по этим темам. Опуская сам процесс выбора и задействованное при этом диалоговое окно, можно только сказать, что после его закрытия поле Selected выбранных тем принимает значение TRUE;
- после того как темы выбраны, все вопросы по всем темам сбрасываются в одну большую коллекцию и как следует тасуются. Поскольку объекты представляются ссылками, то новая коллекция содержит только ссылки, а не сами объекты;
- формируется новый документ с отобранными вопросами в нужном количестве. Документ имеет несколько необычных свойств: он защищен на уровне защиты страницы, в нем отключена проверка правописания (для подавления ненужных визуальных эффектов) и в него внедрены ActiveX-элементы типа checkbox. К каждому элементу динамически прикрепляется обработчик. Задача обработчика - воспринимать ввод пользователя. Как только пользователь выбирает один ответ, другие варианты блокируются и сам вопрос подсвечивается определенным цветом - в зависимости от правильности ответа.
На выходе получается готовый интерактивный документ, который может и должен быть сохранен и одноразово использован в процессе тестирования. Этот документ воспринимает ввод пользователя, видоизменяя сам документ, и в конце концов формирует "протокол тестирования".
Обратите внимание на то, как мы динамически создаем OLE controls в слое документа (есть еще слой векторной графики с абсолютными координатами). Существует также два уровня доступа к OLE-элементу - фактически для каждого элемента создается мини-контейнер со своими собственными свойствами. Для доступа к настоящему OLE приходится обращаться на уровень ниже - к полю Object (это похоже на то, как MFC или Delphi инкапсулирует объекты Windows). К каждому элементу управления "приделывается" персональный и в общем случае ни на что не похожий обработчик - с помощью техники, знакомой конструкторам Wizard'ов и прочих RAD'ов.
Текст основной (а фактически - и единственной) функции приведен в листинге 1. Имя ее не имеет значения - главное, чтобы она вызывалась по Ctrl+K или другим известным способом.
Модуль Module1, экспортируемый из первичного документа и импортируемый в "билет" через файл. Небольшая обработка на предмет "а не закончились ли у нас вопросы?"; если да - то заполнение "протокола" (листинг 2).
Обработчики событий для формы выбора тем - приводится для полноты изложения листинг 3. Сама форма выглядит примерно так, как показано на рисунке.
|