Интеграция VBA в бизнес-приложения независимых разработчиков
Часть 1. Общие сведения о технологии VBA
Технология Application Programmability Component (APC)
Часть 2. Зачем независимым разработчикам нужен VBA
Программируемые приложения — веление времени
Интересы корпоративных заказчиков
Какой вариант автоматизации выбрать
Часть 3. Интеграция VBA в пользовательское VB-приложение
За три минуты? Это вполне реально...
Анализ создаваемого мастером кода
Программный код модуля класса APCIntegration
Создание VBA-проекта в приложении TextEdit
Важные замечания по поводу VBA-проектов
Разбираемся с режимами отладки
Делаем очень полезную макрокоманду
Попытка использования варианта DEBUG VBA 6.0 SDK 6.1
Часть 1. Общие сведения о технологии VBA
Начиная с версии VBA 5.0, которая входила в состав Office 97, Microsoft продвигает этот программный механизм в качестве стандартного средства управления программируемыми приложениями независимых разработчиков. За прошедшие с того момента три года лицензии на его применение приобрели более 150 фирм. Среди них есть и компании с мировым именем (Autodesk, Adobe, PeopleSoft, Baan, SAP, Solomon Software и др.), и небольшие фирмы. Например, в конце прошлого года лицензию на VBA впервые приобрела одна российская компания — екатеринбургская «СКБ Контур» (см. также «Зачем нужен VBA для независимых разработчиков»).
Сначала Microsoft предоставляла лицензии на VBA только разработчикам коммерческих продуктов (ISV — Independent Software Developer). С сентября 1999 года такие лицензии начали распространяться также среди корпоративных заказчиков для создания приложений для внутреннего использования (подробнее об этом см. www.microsoft.ru/offext/officedev/vbasdk/). Ряд российских предприятий (например, страховая компания «Росно») уже ведут тщательное изучение технологии интеграции VBA для практического применения в своих разработках.
Компакт-диск VBA 6.0 SDK 6.1
Технология интеграции VBA основана на использовании специального набора для разработчиков VBA Software Development Kit (SDK). Немного опережая официальное объявление MS Office 2000, весной прошлого года вышел набор VBA 6.0 SDK, который имел целый ряд серьезных расширений по сравнению с версией для VBA 5.0: поддержку немодальных диалоговых окон, полную языковую совместимость с VB 6.0, улучшенную защиту проектов с помощью паролей, возможность интеграции дополнительных модулей (Add-ins) непосредственно в среду разработки и ряд других усовершенствований.
В сентябре появилась новая версия VBA SDK 6.1 (рис. 1), которая, помимо вышеперечисленных функций, обеспечивает слияние модулей для утилиты Windows Installer, содержит более полную документацию, расширенный набор примеров, а также специальный Мастер интеграции VBA в VB-приложения. VBA 6.0 SDK 6.1 сейчас доступен для изучения, и его можно бесплатно заказать на CD-ROM или скачать по адресу: http://msdn.microsoft.com/vba/, где также находится информация о правилах лицензирования и о технической поддержке. Обратите внимание на то, что этот набор является рабочим вариантом — для его практического использования в реальных приложениях нужно будет только купить лицензию.
Перед началом работы с набором VBA SDK следует деинсталлировать все его предыдущие версии. Кроме того, мы рекомендуем сначала ознакомиться с документами, находящимися в разделах Release Notes и Welcome Guide на компакт-диске, и только потом приступить к установке SDK с помощью команды Install Now. После завершения инсталляции запустите программу Vbasetup.exe, чтобы выбрать необходимую вам версию VBA 6.0 — DEBUG или RELEASE (соответственно для отладки и создания окончательных вариантов программ). Следует иметь в виду, что VBA-компоненты отладочной версии несовместимы с приложениями MS Office 2000 (их следует устанавливать только на разные компьютеры). Обратите также внимание, что при работе с VB 6.0 для установки компакт-диска требуется наличие Service Pack 3 на компьютере. В противном случае команда Install Now не сможет инсталлировать Мастер VB Integration Wizard.
Компакт-диск содержит набор документации с описанием различных вариантов интеграции VBA в бизнес-приложения. Там же находятся пять примеров приложений, демонстрирующих интеграцию с VBA с использованием ATL, MFC, многопотоковых DLL и специального мастера VB Integration Wizard.
Технология Application Programmability Component (APC)
Ключевая идея технологии интеграции состоит в том, что механизм VBA вместе со своей средой разработки интегрируется в хост-приложение как внутрипроцессный (in-process) сервер. В результате некоторое готовое бизнес-приложение получает дополнительные возможности создания макрокоманд, аналогичные тем, что реализованы в офисных приложениях Microsoft.
Физически интеграция VBA в хост-приложение выполняется с помощью ключевого инструмента — Microsoft Application Programmability Component (APC), который представляет собой иерархический набор COM-объектов, формирующих промежуточный программный слой для связи с ядром VBA API (рис. 2). Эти объекты содержатся в библиотеке Microsoft APC 6.0 Object Library (Apc60.dll).
APC обеспечивает полную поддержку классов, совместимых с Microsoft Foundation Class (MFS) и Active Template Library (ATL), а также ActiveX-объектов. В состав SDK входит полное руководство Visual Basic APC Reference Manual, содержащее описание набора объектов из библиотеки APC. VB Programmmer’s Guide демонстрирует, как использовать компонент APC для реализации конкретных функций VBA.
Разработчики приложений на основе VB и Delphi должны использовать APC напрямую. При этом для VB-программистов имеется специальный Мастер VB Integration Wizard, который существенно упрощает процесс интеграции. Для разработчиков, применяющих C++ и MFC, в составе VBA 6.0 SDK 6.1 имеются специальные программные средства, называемые соответственно APC/C++ и APC/MFC.
Часть 2. Зачем независимым разработчикам нужен VBA
Программируемые приложения — веление времени
Реализация внутреннего механизма настройки программ и включение в них дополнительных расширений — объективная тенденция развития функциональности самых различных приложений. Это позволяет делегировать конечному пользователю возможности гибкой адаптации программных продуктов в соответствии со спецификой его конкретной задачи.
Еще недавно создание таких средств автоматизации приложений проходило в два логических этапа. Сначала создавался некоторый программный механизм, который использовался разработчиком программного продукта исключительно для его внутренних целей. Унифицированная платформа программирования резко снижала затраты на модернизацию приложений, и у разработчика как бы появлялась возможность получать дополнительную прибыль, не афишируя тот факт, что внесение коррекции в программу делается гораздо проще, чем это было ранее.
Однако в случае успешного развития бизнеса в какой-то момент фирма-разработчик уже просто не успевала отслеживать разнообразные требования потребителей. Тогда она раскрывала свое ноу-хау, передавая средства настройки в руки самих пользователей.
Сегодня процесс создания средств автоматизации в два этапа уже несколько устарел, так как наличие их в бизнес-приложениях становится изначальным обязательным требованием. Более того, это относится не только к коммерческим продуктам (для продажи на рынке), но и к внутрифирменным разработкам.
Интересы корпоративных заказчиков
Довольно многие корпоративные клиенты, которые ведут активную разработку собственных бизнес-программ, сталкиваются с трудностями, связанными с поддержкой единых стандартов приложений для разных подразделений. Поэтому зачастую предпринимаются попытки делегировать разработку этих систем центральной ИТ-группе, которая, как предполагается, должна разбираться во всех приложениях, существующих в данной компании.
Однако в таких ИТ-подразделениях нередко отсутствуют эксперты, разбирающиеся в конкретной области бизнеса, что затрудняет разработку решений. Кроме того, на центральный отдел очень быстро обрушивается целый вал заявок от разных структур корпорации с требованием реализовать ту или иную функцию. Поэтому вопрос о создании приложений, которые могут гибко адаптироваться к нуждам конкретных пользователей в различных подразделениях, является весьма актуальным.
VBA помогает решить эту проблему, предлагая проводить разработки «сверху вниз», что обеспечивает децентрализацию процесса. Тогда ИТ-подразделение может сконцентрироваться на создании базовой инфраструктуры с помощью приложений на базе COM. Затем, интегрировав VBA в эти приложения, компании могут осуществить передачу системы разработчикам подразделений, которые обладают достаточными знаниями в предметной области.
Пример компании «Росно»
Приведем пример российской страховой компании «Росно», где ситуация несколько отличается от описанной выше (там речь шла об очень крупных корпорациях с разветвленной системой ИТ-подразделений). разработкой и поддержкой ПО в «Росно» централизованно занимается департамент информационных технологий. Тем не менее и здесь еще осенью 1999 года разработчики заинтересовались возможностью интеграции VBA в свои собственные приложения, в частности в систему учета договоров страхования.
Как пояснил руководитель проекта Алексей Копылов, проблема создания и сопровождения данной системы заключается в том, что сами договоры и технологии их обработки очень сильно зависят от вида страхования. Поэтому разработчики вынуждены выбирать один из вариантов создания интерфейса с пользователем: либо делать разные программы для различных типов договоров, либо создавать универсальное приложение, которое будет уметь делать все.
Оба варианта имеют недостатки как с точки зрения их разработки и сопровождения, так и с точки зрения их эксплуатации конечным пользователем. Использование же VBA позволяет реализовать третий, компромиссный вариант, когда адаптация приложения выполняется на уровне макрокоманд, то есть быстрое внесение изменений в программу производится без перекомпиляции ее исходного базового модуля. Одновременно резко сокращаются затраты на тестирование, так как макрокоманды не могут повлиять на работоспособность ядра программы. При этом, как отмечают специалисты «Росно», включение VBA не влечет за собой сколь-нибудь серьезного повышения требований к ресурсам рабочих станций.
Какой вариант автоматизации выбрать
Механизм внутреннего программирования нужен; но какой выбрать подход — создавать его самим или взять готовый со стороны? Оба подхода имеют свои плюсы и минусы, конкретный же выбор остается за разработчиком и зависит от специфики решаемой им бизнес-задачи.
Например, российские фирмы («1С», R-Style и некоторые другие) пока явно предпочитают создавать собственную инструментальную платформу. Доводы в пользу такого выбора приводятся довольно веские — существенное снижение (по сравнению с MS Office) требований конечных приложений к ресурсам компьютеров по памяти и быстродействию (что действительно важно для массовых продуктов на российском рынке), повышение оперативности и гибкости в модернизации, а также независимость от внешних поставщиков. Кроме того, здесь надо учитывать наличие в России большого числа сильных программистов, готовых создавать программы на системном уровне за относительно небольшие деньги.
Наверное, такой подход пока вполне оправдан, хотя в стратегической перспективе виден ряд очевидных проблем. Во всяком случае, разработка внутреннего механизма программирования является весьма и весьма трудоемким процессом, одолеть который способны лишь очень сильные коллективы разработчиков. (На Западе даже крупные разработчики предпочитают модель глубокого разделения труда.) И этот путь тем более не под силу корпоративным разработчикам, создающим внутрифирменные приложения.
Здесь нужно отметить, что VBA корпорации Microsoft является не единственным средством внутреннего программирования приложений. Вспомним, что идеология Office/VBA базируется на двух ключевых элементах:
- унифицированной иерархической объектной модели на основе OLE Automation и COM;
- едином средстве программирования VBA.
И если трудно представить VBA без использования объектной COM-модели, то для управления набором COM-объектов можно использовать и другие средства программирования. В связи с этим хотелось бы привести пример программных продуктов фирмы Golden Software (подробнее см. КомпьютерПресс № 10’99 и 02’2000).
В приложениях Surfer и Grapher реализован вариант объектной модели, который в целом соответствует структуре модели MS Office 97/2000. Однако для создания и исполнения скриптов в самих пакетах применяется автономное приложение Scripter 3.0 (рис. 3), которое фактически является облегченным вариантом Visual Basic 6.0, но реализованным не по лицензии Microsoft, а на базе технологий независимых разработчиков — компаний Sax Software и Polar Engineering and Consulting. (Как видим, Golden Software все же реализует принцип разделения труда — она пользуется готовыми решениями других поставщиков.)
Такая технология автоматизации в продуктах Golden Software также имеет свои достоинства и недостатки по сравнению с вариантом интеграции VBA. C одной стороны, эти приложения выглядят очень выигрышно в плане объема дистрибутива и скромных требований к аппаратуре. Но с другой стороны, с точки зрения удобства работы с программами, представляется крайне желательным реализовать более широкие возможности настройки их среды и создания макрокоманд. (Ограничения здесь являются прямым следствием отсутствия внутреннего языка программирования.) Короче говоря, все сколь-нибудь серьезные бизнес-приложения нуждаются в гибких средствах настройки и функционального расширения программ, а в выборе подходящего средства у разработчиков есть разные варианты.
Часть 3. Интеграция VBA в пользовательское VB-приложение
За три минуты? Это вполне реально...
Как мы уже писали выше, для упрощения процедуры интеграции VBA в бизнес-приложения, написанные на Visual Basic 6.0 (с обязательной установкой пакета обновлений SP3), в составе VBA 6.0 SDK 6.1 имеется специальный Мастер. Действительно, после инсталляции VBA SDK в среде разработки VB появляется новый элемент меню Add-Ins — команда VBA Integration Wizard (рис. 4). С ее помощью процесс интеграции VBA в бизнес-приложения, по уверениям Microsoft, должен занять считанные минуты. Ну что же, сейчас мы проверим это на практике...
В качестве предварительного замечания хотелось бы отметить, что мы использовали режим RELEASE набора VBA 6.0 SDK 6.1. Возможно, с применением варианта DEBUG многие проблемы отладки решались бы проще, но у нас нет лишнего компьютера для его автономной загрузки. (Подробнее о DEBUG см. врезку «Попытка использования варианта DEBUG VBA 6.0 SDK 6.1»)
Это наш первый опыт интеграции VBA. В ходе создания данного тестового приложения мы столкнулись с целым рядом вопросов, ответы на которые часто приходилось искать методом проб и ошибок. Может быть, есть более удачные решения, и, наверное, часть проблем объясняется отсутствием необходимой подготовки. Тем не менее...
Создание исходного приложения
Для демонстрации работы мастера VBA Integration Wizard сделаем небольшое VB-приложение, к примеру простой текстовый редактор типа Notebook.
В свое время мы уже писали о том, как создать такое приложение в VB 4.0 и VB 5.0 (см. КомпьютерПресс № 11’97, «Приложение на VB за пять минут. И даже за три!»). Проделайте то же самое в VB 6.0, и вы будете приятно удивлены, узнав, что процесс создания такого редактора займет всего 20-30 секунд! Все, что необходимо, — это запустить VB 6.0, в окне New Project выбрать мастер VB Application Wizard и далее последовательно нажимать кнопку Next в появляющихся диалоговых окнах, оставляя без изменений предлагаемые по умолчанию разные параметры будущего приложения (только лучше еще в первом окне мастера поменять имя Project1 на что-нибудь более конкретное, например на TextEdit).
В результате всех этих операций мы получим проект, который содержит две формы (frmMain.frm и frmDocument.frm) и один модуль (Module1.bas). Форма frmMain имеет четыре элемента управления (Toolbar, StatusBar, ImageList и CommonDialog), а frmDocument — один элемент управления RichTextBox. В них мы ничего менять не будем, кроме названия формы frmMain, для которой изменим свойство Caption на «Текстовый редактор — VB6» (рис. 5).
Теперь запустим наше приложение на выполнение (рис. 6) и обнаружим, что, в отличие от проекта, созданного с помощью аналогичного мастера в VB 5.0, у нас получился практически полнофункциональный текстовый редактор, в котором реализовано большинство основных команд (New, Open, Save, Save As, Cut, Copy, Paste и т.д.). Нажатие только части команд (например, Save All) вызовет появление подобного сообщения: «Add ‘mnuFileSaveAll_Click’ code». Это означает, что в некоторых случаях мастер VB Application Wizard не создает код процедур, соответствующих тем или иным командам, помечая эти места в коде формы комментариями со словами «To DO» и предлагая вам заполнить их своим кодом. При желании вы можете сделать это потом, а пока нас вполне устроит полученный текстовый редактор, который позволяет создавать, редактировать и сохранять документы.
Интеграция VBA
Перейдем к основной части нашего примера, а именно к интеграции VBA в текстовый редактор. Итак, загрузим наш проект TextEdit, а затем запустим в среде команду VBA Integration Wizard.
- Шаг 1. Мастер выводит на экран диалоговое окно Introduction (рис. 7) и сразу же проверяет тип проекта на совместимость с VBA. Приложение с внедренным VBA должно иметь тип ActiveX EXE, чтобы интегрироваться с механизмом VBA на базе COM. Если тип проекта требует изменения, Мастер сообщит о том, что он автоматически поменяет эту установку.
- Шаг 2. На данном шаге (окно Main Form Selection) необходимо задать основную форму пользовательского интерфейса (рис. 8), в нашем случае это форма frmMain. Мастер генерирует код, который будет инициализировать механизм VBA как часть инициализации основной формы. Кроме того, он создает команду Tools|Macro, необходимую для запуска интегрированной среды разработки VBA из бизнес-приложения и доступа к диалоговому окну Macros.
- Шаг 3. Здесь Мастер предлагает указать местонахождение Реестра для хранения VBA-установок (рис. 9). Бизнес-приложение может использовать такие установки совместно с Microsoft Office, если выбирается предложенный по умолчанию ключ Реестра. Заданный же уникальный ключ обеспечивает гарантию того, что проводимые в будущем настройки интегрированной среды разработки (IDE) будут распространяться только на экземпляр VBA, внедренный в бизнес-приложение. Для нашего текстового редактора выберем первый вариант — Use the default VBA registry key.
- Помимо этого окно Language and Registry Options предоставляет возможность выбрать язык, который будет использоваться в среде разработки VBA IDE. К сожалению, русского языка среди предложенных не оказалось, поэтому оставим заданный по умолчанию английский.
- Шаг 4. Выводимое на этом этапе окно Global Object Definition позволяет указать имя глобального объекта для нашего приложения (рис. 10). Данный объект предоставляет удобную точку входа в объектную модель, а обращение к его методам и свойствам осуществляется напрямую, без уточнения имени глобального объекта. Мастер автоматически генерирует модуль класса для глобального объекта, а также создает код для используемых по умолчанию свойств объекта (Application, Name, Parent и VBE). Оставим без изменения предлагаемое имя глобального объекта — CApplication.
- Шаг 5. Мастер выводит диалоговое окно VBA Project Name (рис. 11) и приглашает подтвердить или изменить предлагаемое по умолчанию имя нового VBA-проекта. Оставим без изменения указанное здесь имя — VBAProject.
- Шаг 6. И наконец, последнее окно — Summary of Actions to be Performed (рис. 12) — содержит перечень действий, которые будут выполнены Мастером в процессе интеграции VBA в бизнес-приложение. Что же будет сделано в нашем примере? Мастер предлагает осуществить следующее:
- изменить тип проекта на ActiveX EXE;
- добавить модуль класса APCIntegration для доступа к библиотеке Microsoft APC Library;
- добавить команду меню к форме frmMain для доступа к интегрированной среде разработки VBA IDE;
- добавить команду меню к форме frmMain для доступа к диалоговому окну Macros;
- установить английский язык для среды разработки VBA IDE;
- использовать предлагаемый по умолчанию ключ Реестра;
- создать глобальный объект с именем класса CApplication;
- при запуске открывать проект VBAProject.
Именно эти операции мы задали на предыдущих шагах. Для подтверждения их правильности щелкнем мышью на кнопке Finish, и Мастер сгенерирует соответствующий код.
Анализ создаваемого мастером кода
Запустим проект TextEdit на выполнение (при первом запуске будет выдано окно Project Properties|Debugging, в котором нужно, ничего не меняя, нажать ОК). Теперь наш текстовый редактор обладает некоторыми базовыми функциями, позволяющими использовать VBA, — например переходить в среду разработки VBA IDE (рис. 13). Ситуация крайне любопытная: работая в среде VB, мы запускаем в ней проект, в котором мы можем, в свою очередь, работать еще в одной, очень похожей среде VBA. Но прежде чем осваивать новые возможности TextEdit, попробуем изучить код, созданный Мастером, чтобы воспользоваться этими базовыми функциями.
Как мы отмечали ранее, интеграция VBA базируется на использовании библиотеки APC, которая представляет собой набор COM-объектов. Мастер, по сути дела, формирует начальный программный код для подключения и инициализации основных функций APC в двух созданных им новых модулях класса APCIntegration и CApplication, присоединенных к исходному проекту (рис. 14). Кроме того, Мастер вносит ряд дополнений в уже существовавший код форм. Все программные фрагменты, созданные Мастером, обозначаются логическими скобками в виде комментариев:
‘———————————————————- ‘**** VBA Integration code, begin insert ... ‘**** VBA Integration code, end insert ‘———————————————————-
В модуле основной формы frmMain определяется класс APCIntegration (фактически это объект среды VBA данного приложения):
Public m_apcInt As APCIntegration
а затем в процедуре MDIForm_Load создается экземпляр глобального объекта приложения и выполняются необходимые операции инициализации VBA:
Dim appObj As CApplication ‘ Set m_apcInt = New APCIntegration Set appObj = New CApplication m_apcInt.Initialize _ appObj, Me.hwnd
Кроме того, в модуль формы добавляются новые дополнительные элементы — меню Tools, команды Macros и Visual Basic Editor, а также код для событийных процедур:
Private Sub mnuVbeMenuItem_Click() ‘ Переход в среду VBE m_apcInt.showVBE End Sub Private Sub mnuMacrosMenuItem_Click() ‘ Диалоговое окно Macros будет доступно ‘ только после загрузки проекта m_apcInt.showMacroDialog End Sub Private Sub MDIForm_QueryUnload(cancel As Integer, _ unloadmode As Integer) ‘ Это событие происходит при закрытии формы ‘ или всего приложения m_apcInt.QueryUnload cancel, unloadmode End Sub
Обратите внимание, что описание события QueryUnload отсутствует в MSDN Library Visual Studio 6.0, но включено в MSDN Library MS Office 2000 Developer.
В модуле формы frmDocument также появится новая процедура, созданная Мастером:
Private Sub Form_KeyDown(KeyCode As Integer, _ Shift As Integer) ‘ Обработка комбинаций клавиш Alt+F11 и Alt+F8 If KeyCode = vbKeyF11 And Shift = vbAltMask Then _ frmMain.m_apcInt.showVBE ‘[Alt][F11] If KeyCode = vbKeyF8 And Shift = vbAltMask Then _ frmMain.m_apcInt.showMacroDialog ‘[Alt][F8] End Sub
Дело в том, что Menu Editor в среде VB не поддерживает использование комбинаций Alt+KEY для «горячих» клавиш, поэтому для их обработки требуется писать приведенный выше код.
Программный код модуля класса APCIntegration
Модуль класса APCIntegration имеет значение свойства Instancing=Private и включает четыре внутренних объекта: General, Class, m_apcHost и m_ApcProject (рис. 15).
Подпрограммы секции General представляют собой реализацию методов объекта APCIntegration:
Метод |
Действие |
---|---|
Initialize |
Инициализация APC и VBA |
showVBE |
Вывод интегрированной среды разработки (VBE) |
QueryUnload |
Аккуратное закрытие системы |
showMacroDialog |
Вывод диалогового окна Macros для работы с макрокомандами VBA |
CreateNewCleanProject |
Создание нового VBA-проекта |
OpenProj |
Загрузка существующего VBA-проекта |
SaveProj |
Сохранение VBA-проекта |
В этом модуле также создается экземпляр объекта Apc, занимающий самое верхнее положение в иерархии объектов библиотеки MSAPC:
Private WithEvents m_apcHost As MSAPC.Apc Private Sub Class_Initialize() Set m_apcHost = New Apc End Sub
Затем выполняется начальная установка свойств объекта Apc (обращение к этому методу производится из процедуры MDIForm_Load):
Public Sub Initialize(appObj As CApplication, _ hwnd As Long) ‘ hWnd (внутренний идентификатор объектов Windows) ‘ для хост-приложения m_apcHost.hwnd = hwnd ‘ ‘ глобальный объект приложения m_apcHost.ApplicationObject = appObj ‘ ‘ имя хост-приложения для ссылки на него из среды VBE m_apcHost.HostName = App.Title ‘ ‘ ключ для лицензирования m_apcHost.LicenseKey = _ "16175148714896599659AFABD8ED3C2A416B45E4CD6F5484BD8CED" ‘ ‘ ключ Реестра, где будут храниться настройки среды IDE m_apcHost.RegistryKey = "Common" ‘ ‘ кодовая таблица, используемая в VBA IDE m_apcHost.Locale = 1033 ‘ ‘ фильтр файлов для диалогового окна References в VBA m_apcHost.FileFilter = "All Reference Files _ (*.olb, *.tlb, *.dll, *.exe, *.ocx)" & Chr$(0) & _ "*.olb;*.tlb;*.dll;*.exe;*.ocx" & Chr$(0) ‘ ‘ создание проекта по умолчанию Set m_ApcProject = m_apcHost.Projects.Add( _ axProjectNormal, "VBAProject") ‘ ‘ установка флага проекта как False (Ложь), ‘ благодаря чему приглашение сохранить проект ‘ не будет выдаваться до тех пор, пока пользователь ‘ не произведет какие-либо изменения m_ApcProject.Dirty = False End Sub
Вот основные параметры объекта Apc, которые нужно установить:
Свойство |
Действие |
---|---|
Application |
Возвращает объект приложения, связанный с текущим объектом. В данном случае объект просто возвращает сам себя |
Parent |
Возвращает прямой родительский объект текущего объекта. В данном случае объект просто возвращает сам себя |
Name |
Возвращает имя приложения |
VBE |
Возвращает объектной модели VBE интерфейс самого высокого уровня |
Но это далеко не все свойства объекта Apc — пользователь может дополнить код процедуры, чтобы сделать нужные установки по собственному разумению.
В этом же модуле реализован ряд других методов объекта APCIntegration. Так, чтобы вывести на экран или сделать невидимой среду разработки VBA IDE, следует воспользоваться свойством Visible объекта Ide компонента APC:
Public Sub showVBE() m_apcHost.Ide.Visible = True End Sub
Управление набором открытых VBA-проектов осуществляется при помощи коллекции Projects из библиотеки APC. Для создания нового VBA-проекта просто вызовите Apc.Projects.Add и сохраните результат в переменной MSAPC.Project. Мастер VBA Integration Wizard помещает следующий код в модуль класса APCIntegration:
Private WithEvents m_ApcProject As MSAPC.Project Set m_ApcProject = m_apcHost.Projects.Add(ProjectGlag, _ "VBAProject")
ProjectFlag представляет собой параметр, определяющий тип создаваемого приложения:
Константа |
Значение |
Действие |
AxProjectNormal |
0 |
Создает стандартный проект |
AxProjectHidden |
1 |
Создает скрытый проект |
AxProjectDisable-Macros |
2 |
Создает проект, но отключает все макрокоманды |
AxProjectDisable-Save |
4 |
Отключает команду Save в меню File вместе с соответствующей кнопкой панели в среде разработки VBA IDE |
AxProjectThrow-AwayCompiled- State |
8 |
Выбрасывает весь скомпилиро- ванный код из VBA-проекта во время загрузки проекта |
Создание VBA-проекта в приложении TextEdit
Мы немного разобрались в программном коде, созданном Мастером для интеграции VBA в наше приложение TextEdit. Теперь попробуем воспользоваться новыми возможностями VBA (если они действительно появились).
Запустим TextEdit. Как известно, с точки зрения хост-приложения VBA-проект представляет собой набор макрокоманд. Попробуем создать какую-нибудь макрокоманду, выбрав команду Tools|Macros. Однако тут нас ожидает неудача — в окне Macros все командные кнопки, кроме Cancel, являются недоступными (рис. 16).
Тогда поступим иначе. Перейдем в среду VBE (команда Tools|VBE) и попытаемся сделать что-нибудь там. В окне Project мы увидим, что здесь содержится некий проект VBAProject, в котором нет ни одного программного компонента (рис. 17). Теперь командой Tools|Macros создадим макрокоманду (при этом автоматически создастся программный модуль) с названием MyMacro, в которой напишем всего одну строку:
MsgBox "Моя макрокоманда работает в среде VBE!"
Эта макрокоманда доступна через команду Tools|Macros как из VBE, так и из TextEdit. Но обратите внимание: создавать новые макрокоманды из среды TextEdit можно только при уже существующем программном модуле в VBAProject. Теперь среда VBE приняла более знакомый вид (рис. 18), поэтому дальнейшая логика работы в принципе понятна: пишем код в процедурах программного модуля, создаем формы, размещаем на них элементы управления и т.д.
Важные замечания по поводу VBA-проектов
Наша макрокоманда MyMacro действительно выполняется, но она пока не умеет делать ничего полезного для редактора TextEdit. Возникает вопрос: каким образом создаваемый нами программный проект связан с текстовым редактором и как он будет взаимодействовать с обрабатываемыми там документами?
Отвечая на него, мы сейчас попытаемся написать какой-нибудь полезный для нашего исходного приложения программный макрокод в VBE, но для этого нам придется сохранять создаваемый проект, чтобы вносить некоторые коррективы в исходную VB-программу. И тут мы встретимся с некоторыми проблемами, в которых нужно разобраться.
Давайте вспомним, как обстоит дело в Word 97/2000. Там и документ, и проект хранятся в одном файле (дополнительные программы можно загрузить в виде шаблонов). Здесь же мы имеем дело с двумя разными файлами. Чтобы убедиться в этом, выполните такие операции:
- В среде VBE (File|Save VBAProject) сохраним проект под именем MyVBAPr.vba. Затем вернемся в среду TextEdit, запишем в текущий пустой документ фразу «Документ 1» и сохраним его под именем Doc1.rtf. Закроем приложение TextEdit (то есть вернемся в среду VB).
- Опять запустим на выполнение TextEdit и загрузим Doc1.rtf. Перейдем в VBE, где вновь увидим пустой проект VBAProject. Попробуем загрузить MyVBAPr.vba (File|Open Project), но не тут-то было: выдается сообщение об ошибке «Path/File access error». Что же делать? Давайте разбираться...
Дело в том, что VBE использует два типа проектов. (Они разные, хотя и имеют одинаковое расширение .VBA, что вносит явную путаницу!) С первым из них мы уже ознакомились: в окне Project он всегда называется стандартно — VBAProject (их может быть несколько). Именно этому компоненту соответствует объект m_ApcProject, который создается в процедуре Initialize модуля APCIntegration.
Второй тип VBA-проекта создается VBE командой File|New Project (мы будем называть его просто Project в отличие от VBAProject). Такой проект можно сохранять и открывать заново, использовать несколько вариантов одновременно. В нем можно записывать программные модули и формы и т.д. Но вот в чем проблема: в качестве макрокоманд нашего основного приложения могут использоваться только процедуры VBAProject — подпрограммы Project являются недоступными (рис. 19). (Кстати, аналогичная ситуация возникает с автономными VBA-проектами в MS Office 2000. Так, например, в Word 2000 роль VBAProject выполняют файлы документов и шаблонов.) Таким образом, сейчас нас в первую очередь интересует возможность создания и использования кода в VBAProject, но мы не можем загружать ранее созданные проекты данного типа.
Загрузка VBAProject
Для решения данной задачи нам нужно внести дополнения в созданный мастером VB Integration Wizard программный код. Для этого закроем приложение TextEdit и вернемся в среду VB. В модуль класса APCIntegration запишем такую процедуру открытия существующего VBAProject:
Friend Sub OpenNewVBAProject() Dim stg As MSAPC.Storage, stFilter As String, stFileName As String ‘ stFilter = "VBA Files (*.vba)" & Chr$(0) & "*.VBA" & Chr$(0) _ & "All Files (*.*)" & Chr$(0) & "*.*" & Chr$(0) stFileName = FileOpenSave(OFN_NOCHANGEDIR, CurDir$, _ stFilter, , ".VBA", , , m_apcHost.hwnd, True) If Len(stFileName) > 0 Then Set stg = OpenStorage(stFileName) Set m_ApcProject = m_apcHost.Projects.Open _ (axProjectNormal, stg) m_stFileName = stFileName End If End Sub
Кроме того, откорректируем процедуру Initialize этого же модуля. Там нужно заменить строку:
‘ Create a default project Set m_ApcProject = m_apcHost.Projects.Add(axProjectNormal, _ "VBAProject")
на такой код:
Dim msgResult As VbMsgBoxResult msgResult = MsgBox("Загрузить ранее созданный VBAProject?", _ vbYesNo, App.Title) If msgResult = vbYes Then Call OpenNewVBAProject ‘ открыть существующий проект End If If Len(m_stFileName) = 0 Then ‘ Create a default project Set m_ApcProject = m_apcHost.Projects.Add _ (axProjectNormal, "VBAProject") End If
Итак, теперь вместо создания объекта m_ApcProject по умолчанию мы предлагаем пользователю при запуске приложения выбрать вариант загрузки существующего проекта. Можно также включить в меню TextEdit специальную команду для загрузки существующего VBAProject — тогда можно одновременно загружать несколько проектов этого типа.
Однако здесь есть еще один важный момент. Как выяснилось, загружать созданный нами VBAProject (в данном случае MyVBAPr.vba) можно только в рамках одной сессии Visual Basic! Так что если вы сейчас закроете VB6, а потом запустите его вновь, откроете проект TextEdit и попробуете загрузить MyVBAPr, то появится сообщение об ошибке, связанной с несоответствием версий исполняемого модуля и загружаемого расширения (рис. 20).
При работе с EXE-модулем приложения TextEdit такой проблемы с повторным использованием VBAProject не возникает даже после изменения и перекомпиляции программы.
Отсюда вывод: выполняя данный пример, не закрывайте среду VB6, а перед закрытием или созданием EXE-модуля приложения сохраните все программные компоненты проекта с помощью импорта их в отдельные файлы для последующего восстановления.
Разбираемся с режимами отладки
Итак, наше обновленное приложение TextEdit теперь может загружать файлы нескольких видов: чисто текстовые документы, а также VBA-проекты типа VBAProject и Project. Связь же между средой собственно редактора с VBE может осуществляться путем обращения к макрокомандам VBAProject. К данному моменту мы написали только одну макрокоманду, которая не имеет никакого функционального отношения к TextEdit: она лишь может заявить о том, что действительно получает управление и готова выполнять наш код. Попробуем изобразить в ней что-нибудь полезное.
Сейчас мы находимся в среде VB6 с загруженным проектом TextEdit. Запустим его на выполнение; далее, ответив положительно на вопрос «Загрузить ранее созданный VBAProject?», загрузим MyVBAPr и перейдем в среду VBE.
Вспомним, что связь макрокода в среде VBE с самими офисными программами и их документами в MS Office выполняется через систему объектов. Какие же объекты TextEdit доступны нам? Для ответа на этот вопрос откроем окно Object Browser (F2) и в списке Project/Library выделим только TextEdit (рис. 21). Здесь видно, что наш проект содержит объект CApplication, имеющий четыре компонента: Application, Parent, Name и VBE. Модернизируем нашу макрокоманду MyMacro, чтобы прочитать параметр Name, который, как видно из пояснения в Object Browser, является свойством символьного типа:
MyVar$ = TextEdit.CApplication.Name MsgBox "Имя = " & MyVar$
Однако при попытке выполнить макрокоманду появится сообщение о том, что компонент CApplication не найден.
Как нам представляется, проблема заключается в том, что отладка ActiveX-компонентов в среде VB возможна только при использовании режима ActiveX DLL, а мы сейчас работаем с вариантом EXE (мы обращали на это внимание в статье «Создание ActiveX-серверов в VB 5.0», КомпьютерПресс № 12’97, с.134-136). Вероятно, данная проблема решается при использовании VBA SDK в варианте DEBUG, мы же работаем в режиме RELEASE.
Поэтому далее мы будем вынуждены поступать следующим образом: проводить изменения в среде VB, затем создавать выполняемый модуль TextEdit.exe и отлаживать макрокоманды уже в нем. Это, конечно, очень неудобно, так как теряются возможности отладки в среде VB, но другого способа мы не придумали.
Однако, запустив на выполнение MyMacro в среде EXE-модуля, мы опять увидели странное сообщение об ошибке в том же операторе макрокода: «Нельзя создать вторую MDI-форму». При выполнении аналогичного примера с SDI-формой появлялась ошибка о неопределенном объекте.
Как выяснилось, проблема заключается в программном коде, сформированном Мастером Application Wizard при начальном создании редактора. Оказывается, что подобная конструкция инициализации формы:
Public fMainForm As frmMain Sub Main() Set fMainForm = New frmMain fMainForm.Show End Sub
вступает в противоречие с кодом, полученным в результате работы VB Integration Wizard в модуле CApplication:
Public Property Get Name() As String Name = frmMain.m_apcInt.ApcHost.HostName End Property
так как в данном случае делается попытка создания второго экземпляра формы. Проблема решается заменой исходного кода процедуры Main на следующий вариант:
Sub Main() Load frmMain frmMain.Show End Sub
Кроме того, командой Replace необходимо заменить идентификатор fMainForm на frmMain во всех модулях нашего проекта.
После такой коррекции макрокоманда MyMacro начинает правильно работать и выдавать в окне сообщения название нашего проекта — TextEdit. Это уже кое-что полезное!
Создание ActiveX-компонентов
Итак, идея взаимодействия VBA-проектов из среды VBE с нашим исходным приложением становится достаточно понятной: макрокод работает с ActiveX-компонентами приложения. И наши возможности управления приложением из среды VBE полностью зависят от функциональности ActiveX-компонентов.
VB Integration Wizard создал модуль класса CApplication, который предназначается именно для формирования одноименного объекта, через свойства и методы которого обеспечивается взаимодействие макрокода с исходным приложением. Мастер записал туда минимальный код, который пока никак не отражает специфику конкретной прикладной программы и позволяет получать только самые общие сведения о ней в виде свойств объекта CApplication:
Public Property Get Application() As CApplication ‘ Возвращает объект приложения, связанный с текущим объектом. ‘ В данном случае объект просто возвращает сам себя. Set Application = Me End Property Public Property Get Name() As String ‘ Возвращает имя приложения Name = frmMain.m_apcInt.ApcHost.HostName End Property Public Property Get Parent() As CApplication ‘ Возвращает прямой родительский объект текущего объекта. ‘ В данном случае объект просто возвращает сам себя. Set Parent = Me End Property Public Property Get VBE() As Object ‘ Возвращает интерфейс самого высокого ‘ уровня для работы с объектной моделью VBE Set VBE = frmMain.m_apcInt.ApcHost.VBE End Property
Таким образом, возможности реального взаимодействия макрокода с нашим текстовым редактором полностью зависят от того кода, который нам придется писать.
Делаем очень полезную макрокоманду
Пусть, например, мы хотим дать пользователю возможность управлять цветом фона текстовых окон документов в редакторе. В этом случае нужно добавить в модуль класса CApplication такой код для чтения-записи нового свойства BackColor:
Property Get BackColor() As Long ‘ Чтение кода цвета фона текстового окна ‘ текущего документа редактора BackColor = frmMain.ActiveForm.rtfText.BackColor End Property Property Let BackColor(NewBackColor As Long) ‘ Установка цвета фона текстового окна ‘ текущего документа редактора If NewBackColor < 0 Then ‘ установка по умолчанию frmMain.ActiveForm.rtfText.BackColor = _ RGB(250, 250, 250) Else ‘ новое значение frmMain.ActiveForm.rtfText.BackColor = NewBackColor End If End Property
Теперь, создав загрузочный модуль TextEdit.exe, мы можем писать VBA-проекты, которые будут использовать новое свойство объекта, в частности корректируя его с помощью следующей макрокоманды:
Sub ModifyBackFont() ‘ Чтение и коррекция цвета фона текстового окна ‘ текущего документа редактора TextEdit Dim BackColor&, a$ BackColor = TextEdit6.CApplication.BackColor a$ = "Коррекция цвета фона" & vbCrLf & _ "текущего текстового документа." & vbCrLf & vbCrLf & _ "Введите новое значение (16-ричное)" a$ = InputBox(a$, _ "Макрокоманда ModifyBackFont среды VBE",_ Hex$(BackColor)) If a$ <> "" Then ‘ установка нового значения фона a$ = "&h" + a$ TextEdit.CApplication.BackColor = CLng(a$) End If End Sub
Сохраняем VBAProject (MyVBAPr.vba), закрываем приложение TextEdit, а затем и среду VB, сохранив исходный код проекта TextEdit.vbp.
Поздравляем всех, кто вместе с авторами добрался до этого места в нашем программном примере! Нам впервые удалось создать собственное приложение с интегрированным в него VBA (рис. 22). Еще немного усилий — разработать объектную модель и написать соответствующие ActiveX-компоненты — и мы получим текстовый редактор, не уступающий по функциональным возможностям и гибкости программирования MS Word 2000. Но это — немного позднее. Сейчас же можно подвести некоторые предварительные итоги.
Подведение итогов
В ходе выполнения нашего примера мы проделали следующие основные операции:
- Создали исходное VB-приложение (TextEdit) с помощью мастера VB Application Wizard.
- Выполнили в среде Visual Basic интеграцию VBA в исходное приложение. Это было выполнено в виде создания в VB-проекте двух модулей класса: APCIntegration — инициализация и связь со средой VBE, CApplication — свойства и методы объекта исходного приложения. Мастер внес также некоторые коррективы в исходные программные модули проекта.
- Вручную внесли дополнения в программный код frmMain и APCIntegration, чтобы обеспечить возможность загрузки сохраненного ранее VBA-проекта.
- Вручную откорректировали код frmMain и frmDocument, чтобы порядок инициализации главной формы проекта стал совместимым с VBE.
- Написали простой код для управления свойством объекта приложения (цвет фона окна с текстовым документом) и создали исполняемый модуль TextEdit.exe.
- В исходном приложении TextEdit.exe появилась среда VBE, с помощью которой мы написали макрокоманду для управления цветом фона окнами текстового редактора.
Подготовка данного примера (анализ возникающих проблем, изучение документации, поиск нужных решений и т.д.) заняла у нас несколько дней. Повторить же проделанный путь — создание с нуля простого, но многооконного текстового редактора с интегрированной средой VBE и формирования в нем VBA-проекта с макрокомандой ModifyBackColor — можно всего за несколько минут.
В отсутствие VBA SDK подобную задачу нам бы пришлось решать двумя способами:
- Использование среды Visual Basic (или другой программы типа VBScripter).
В этом случае мы бы действовали так:
- создали исходное VB-приложение в виде ActiveX-сервера (EXE-модуль);
- загрузили среду Visual Basic и создали в ней проект (аналог макрокода), который бы работал с ActiveX-компонентами внешнего приложения.
Подобный вариант имеет такие недостатки:
- каждому пользователю придется ставить на компьютер VB;
- отсутствует связь пользовательского приложения со средой VB (VB не является ActiveX-сервером).
- Внесение расширений в приложение в виде прямой коррекции его исходного кода.
Недостатки данного способа очевидны: такие исправления может делать только один человек — автор программы.
Разумеется, возможность практического использования интеграции VBA в бизнес-приложение упирается в необходимость создания разработчиком соответствующей системы ActiveX-объектов, но это уже совсем другая проблема.
Здесь следует обратить внимание на то, что наше приложение TextEdit работает с исходными файлами двух типов:
- Текстовые документы (TXT и RTF), для обработки которых и предназначено приложение.
- VBA-проекты, которые загружаются в среде VBE и позволяют расширять функции исходного приложения (созданного в VB 6.0).
Возможен и вариант, реализованный в программах MS Office, когда VBA-код хранится вместе с обрабатываемым документом. Именно такой подход реализован в приложении SdiNote, которое входит в качестве примера в VBA 6.0 SDK 6.1.
И все же...
В целом идея интеграции VBA в приложения независимых разработчиков представляется очень интересной и перспективной. Но чтобы сделать эту технологию доступной для широкого круга клиентов, Microsoft нужно серьезно поработать над повышением качества документации, особенно в отношении примеров реализации проектов, а также над созданием более эффективных Мастеров интеграции.
КомпьютерПресс 3'2000