Подвижный 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) - то есть веса вопросов на вес полученного ответа. Успеваемость экзамена вычисляется как процент набранных баллов от максимально возможных. Дополнительные условияДля того чтобы программа была полезной, она должна работать в условиях реального мира. В данном случае заказчик поставил следующие требования:
Итак, отталкиваясь от всего перечисленного, начнем строить нашу тестовую систему. Первое, что приходится сделать, это выбрать Word как в качестве источника данных, так и в качестве среды выполнения. Такое решение - самое естественное, поскольку ни на какой другой рантайм мы не можем рассчитывать. Первой идеей было использовать текстовый режим и gcc+couses. Но редактирование текстовых файлов в dos-кодировке - это задача, с которой справится не каждый современный пользователь, и, как говорят, "текстовый режим выглядит не современно". Первое последствие из выбора Word в качестве инструмента: наша база данных будет представлена обычным Word-документом определенного формата. В силу некоторых исторических причин формат текста был следующим: &Вопросы для системного администратора"100" Чем нужно зажимать сетевой джек: "80" Что вы делали на Новый Год: "100" Как у вас проложен сетевой кабель: То есть первая строка со значком & обозначает начало темы, потом идут группы строк, первая из которых - вопрос, а остальные - ответы. Строки предваряются весовыми коэффициентами: "важности" - для вопросов, и "степени верности" - для ответов. Согласитесь, что ввести и модифицировать такой файл может любой пользователь. Для представления банка данных создадим три тривиальных класса: ' 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 Как видите, вопрос - это собственно вопрос и коллекция ответов, а тема - это название темы и коллекция вопросов. Это похоже на представление списков в LISP - голова и хвост. Самый главный кирпич всей иерархии, класс BLline, включает в себя строку и ее вес, а также дополнительные поля, смысл которых прояснится позже. Тривиальный метод Perse принимает строку и преобразует ее в поля объекта - немудреный суржик перегрузки конструктора в C++. Перед тем как собственно выбирать и тасовать вопросы и ответы, построим древовидную структуру, несколько напоминающую DOM-представление,- для этого вызывается специальная функция. Предполагается, что некто, ответственный за составление вопросов, активно читает и исправляет их - и, когда наступает компромисс между совестью и усталостью, формирует нужное количество именных "конвертов" с вопросами для каждого студента. Есть четыре возможности запустить макрос: по нажатию горячей клавиши, по нажатию кнопки на панели, по системному событию и явно через меню Макросы. Поскольку этот вопрос нас пока не занимает, то пусть наш процесс запускается по Ctrl+K - кнопки на панелях инструментов имеют свойство теряться, а сами панели - быть закрытыми шаловливыми конечностями пользователей. С высоты птичьего полета выполняем такие вот действия:
На выходе получается готовый интерактивный документ, который может и должен быть сохранен и одноразово использован в процессе тестирования. Этот документ воспринимает ввод пользователя, видоизменяя сам документ, и в конце концов формирует "протокол тестирования". Обратите внимание на то, как мы динамически создаем OLE controls в слое документа (есть еще слой векторной графики с абсолютными координатами). Существует также два уровня доступа к OLE-элементу - фактически для каждого элемента создается мини-контейнер со своими собственными свойствами. Для доступа к настоящему OLE приходится обращаться на уровень ниже - к полю Object (это похоже на то, как MFC или Delphi инкапсулирует объекты Windows). К каждому элементу управления "приделывается" персональный и в общем случае ни на что не похожий обработчик - с помощью техники, знакомой конструкторам Wizard'ов и прочих RAD'ов. Текст основной (а фактически - и единственной) функции приведен в листинге 1. Имя ее не имеет значения - главное, чтобы она вызывалась по Ctrl+K или другим известным способом. Модуль Module1, экспортируемый из первичного документа и импортируемый в "билет" через файл. Небольшая обработка на предмет "а не закончились ли у нас вопросы?"; если да - то заполнение "протокола" (листинг 2). Обработчики событий для формы выбора тем - приводится для полноты изложения листинг 3. Сама форма выглядит примерно так, как показано на рисунке.
Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=4788 |