При создании собственных приложений вам наверняка хотелось бы, чтобы конечный пользователь мог щелчком мыши открыть среду VBA и программировать там так же, как в пакетах Microsoft Office. Но как это реализовать?

В предыдущей статье «Windows-приложение за пять минут» («Мир ПК», № 8/2000, с. 80) мы пообещали рассказать об интеграции созданных проектов с VBA, что сейчас и сделаем. Но для начала немного о том, что такое VBA и где его можно использовать.

Общие сведения о технологии VBA

Уже с версии VBA 5.0, входившей в состав Office 97, Microsoft продвигает этот механизм в качестве стандартного средства управления программируемыми приложениями независимых разработчиков. За последние три года лицензии на применение VBA приобрели более 150 фирм, в том числе и всемирно известные компании (Autodesk, Adobe, PeopleSoft, Baan, SAP, Solomon Software и др.), и небольшие. В конце 1999 г. лицензию на VBA впервые приобрела российская компания — екатеринбургская фирма «СКБ Контур».

Сначала Microsoft предоставляла лицензии на VBA только разработчикам коммерческих продуктов (ISV — Independent Software deVeloper), но с сентября 1999 г. они стали распространяться и среди корпоративных заказчиков (подробнее см. www.microsoft.ru/offext/officedev/vbasdk/).

Программируемые приложения — веление времени

Реализация внутреннего механизма настройки программ и включение в них дополнительных расширений — тенденция развития функциональности самых различных приложений. Это позволяет делегировать конечному пользователю возможности гибкой адаптации программных продуктов.

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

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

Как дела у корпоративных заказчиков?

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

VBA помогает решить проблему, позволяя проводить разработку «сверху вниз», что обеспечивает децентрализацию процесса. Тогда ИТ-подразделение сможет сконцентрироваться на создании базовой инфраструктуры с помощью приложений на базе COM. Затем, интегрировав VBA в эти приложения, можно передать систему экспертам, обладающим достаточными знаниями в предметной области.

Какой вариант автоматизации выбрать?

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

Многие российские фирмы пока явно предпочитают создавать собственную инструментальную платформу. Доводы в пользу такого выбора приводятся довольно веские — существенное снижение (по сравнению с MS Office) требований конечных приложений к ресурсам компьютеров (что действительно важно для массовых продуктов на российском рынке), повышение оперативности и гибкости модернизации, независимость от внешних поставщиков. Кроме того, здесь надо учитывать наличие в России большого числа квалифицированных программистов, готовых делать программы на системном уровне за невысокую плату.

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

VBA не единственное средство внутреннего программирования приложений. Вспомним, что идеология Office/VBA базируется на двух ключевых элементах:

  • унифицированной иерархической объектной модели на основе OLE Automation и COM;
  • едином средстве программирования VBA.

И если трудно представить VBA без использования объектной COM-модели, то для управления набором COM-объектов можно применять и другие средства программирования, как это реализовано, например, в программных продуктах фирмы Golden Software.

Компакт-диск VBA 6.0 SDK 6.1

Технология интеграции VBA основана на использовании специального набора для разработчиков VBA Software Development Kit (SDK). Немного опережая официальное объявление MS Office 2000, весной 1999 г. вышел набор VBA 6.0 SDK, обладающий рядом дополнительных расширений по сравнению с версией для VBA 5.0, а именно поддержкой немодальных диалоговых окон, полной языковой совместимостью с VBA 6.0, улучшенной защитой проектов с помощью паролей, возможностью интеграции дополнительных модулей (Add-Ins) непосредственно в среду разработки и др.

В сентябре появилась новая версия VBA 6.0 SDK 6.1, которая помимо перечисленных выше функций обеспечивает слияние модулей для утилиты Windows Installer, содержит более полную документацию и расширенный набор примеров, а также специальный мастер по интеграции VBA в VB-приложения. VBA 6.0 SDK 6.1 сейчас доступен для изучения, и компакт-диск можно бесплатно заказать или скопировать по адресу 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 не сможет инсталлировать мастер VBA Integration Wizard.

На компакт-диске также содержатся набор документации с описанием различных вариантов интеграции VBA в бизнес-приложения и в качестве примеров пять приложений, демонстрирующих интеграцию с VBA с использованием ATL, MFC, многопотоковых DLL и специального мастера VBA Integration Wizard.

Технология Application Programmability Component (APC)

Основная идея технологии интеграции состоит в том, что механизм VBA вместе со своей средой разработки интегрируется в приложение как внутрипроцессный (in-process) сервер. В результате готовое бизнес-приложение получает дополнительные возможности для создания макрокоманд, аналогичные реализованным в офисных приложениях Microsoft.

Физически интеграция VBA выполняется с помощью ключевого инструмента — Microsoft Application Programmability Component (APC), который представляет собой иерархический набор COM-объектов, формирующих промежуточный программный слой для связи с ядром VBA API (рис. 1). Эти объекты располагаются в библиотеке 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. Как использовать компонент APC для реализации конкретных функций VBA, демонстрирует VB Programmer?s Guide.

Разработчики приложений на основе VB и Delphi должны обращаться к APC напрямую. При этом для VB-программистов имеется специальный мастер VBA Integration Wizard, существенно упрощающий процесс интеграции. Программисты на С++ и MFC могут использовать специальные программные средства, входящие в состав VBA 6.0 SDK 6.1, называемые APC/C++ и APC/MFC соответственно.

Интеграция VBA в пользовательское VB-приложение

Рис. 2

После инсталляции VBA 6.0 SDK 6.1 в среде разработки VB появляется новый элемент меню Add-Ins — команда VBA Integration Wizard (рис. 2). С ее помощью процесс интеграции VBA в бизнес-приложения, по уверениям Microsoft, должен занять считаные минуты. Проверим это на практике.

Примечание. При установке набора VBA 6.0 SDK 6.1 мы выбрали вариант RELEASE — создание окончательных вариантов программ.

Пример интеграции VBA

Для демонстрации работы мастера VBA Integration Wizard используем простой текстовый редактор, созданный в статье «Windows-приложение за пять минут» (надеемся, что вы его сохранили). Загрузим наш проект TxtEdit.vbp в среду разработки VB, а затем запустим там команду VBA Integration Wizard.

Рис. 3

Шаг 1. Мастер выводит на экран диалоговое окно Introduction (рис. 3) и сразу же проверяет тип проекта на совместимость с VBA. Чтобы интегрироваться с механизмом VBA на базе COM, приложение с внедренным VBA должно иметь тип ActiveX EXE. Если тип проекта требует изменения, мастер сообщит о том, что автоматически поменяет эту установку.

Рис. 4

Шаг 2. В окне Main Form Selection необходимо задать основную форму пользовательского интерфейса (рис. 4), в данном примере это frmEdit. Мастер создает процедуру, которая будет запускать механизм VBA как часть инициализации основной формы, а также команду Tools|Macro для запуска интегрированной среды разработки VBA из бизнес-приложения и доступа к диалоговому окну Macros.

Рис. 5

Шаг 3. Мастер предлагает указать местонахождение Реестра для хранения VBA-установок (рис. 5). Бизнес-приложение может использовать такие установки совместно с Microsoft Office, если выбирается предложенный по умолчанию ключ Реестра. Заданный же уникальный ключ гарантирует, что проводимые в будущем настройки интегрированной среды разработки (IDE) будут распространяться только на экземпляр VBA, внедренный в бизнес-приложение. Для нашего текстового редактора возьмем первый вариант — Use the default VBA registry key.

Помимо этого в окне Language and Registry Options можно выбрать язык для среды разработки VBA IDE. К сожалению, русского среди предложенных не оказалось, поэтому оставим английский.

Рис. 6

Шаг 4. Окно Global Object Definition позволяет указать имя глобального объекта для приложения (рис. 6). Данный объект предоставляет удобную точку входа в модель, а обращение к его методам и свойствам осуществляется напрямую, без уточнения имени этого объекта. Мастер автоматически генерирует модуль класса для глобального объекта, а также создает используемые по умолчанию свойства объекта (Application, Name, Parent и VBE). Заменим предлагаемое имя глобального объекта — CApplication на Application.

Рис. 7

Шаг 5. Мастер выводит диалоговое окно VBA Project Name (рис. 7) и приглашает подтвердить или изменить устанавливаемое по умолчанию имя нового VBA-проекта. Оставим также неизменным указанное здесь имя — VBAProject.

Рис. 8

Шаг 6. В последнем окне, Summary of Actions to be Performed (рис. 8), содержится перечень действий, которые будут выполнены мастером в процессе интеграции VBA в бизнес-приложение. Что же будет сделано в нашем примере? Мастер предлагает следующее:

  • изменить тип проекта на ActiveX EXE;
  • добавить модуль класса APCIntegration для доступа к библиотеке Microsoft APC Library;
  • вставить модуль MainModule;
  • добавить процедуру Sub Main к модулю MainModule для запуска объекта;
  • дополнить процедуру Sub Main текстом, запускающим форму frmEdit;
  • добавить команду меню к форме frmEdit для доступа к интегрированной среде разработки VBA IDE;
  • добавить команду меню к форме frmEdit для доступа к диалоговому окну Macros;
  • установить английский язык для среды разработки VBA IDE;
  • использовать предлагаемый по умолчанию ключ Реестра;
  • создать класс Application для использования в качестве объекта самого верхнего уровня (объекта Application);
  • установить глобальный объект с именем класса Application;
  • при запуске открывать проект VBAProject.

Именно эти операции мы задали на предыдущих шагах. Чтобы подтвердить их правильность, щелкнем на кнопке Finish, и мастер сгенерирует соответствующие процедуры.

Что создал мастер?

Запустим проект TxtEdit.vbp на выполнение (при первом запуске появится окно Project Properties|Debugging, где нужно, ничего не изменяя, нажать ОК). Теперь наш текстовый редактор обладает определенными базовыми функциями, позволяющими использовать VBA, например переходить в среду разработки VBA IDE. Ситуация любопытная: работая в среде VB, мы запускаем в ней проект, и там можем, в свою очередь, работать еще в одной, очень похожей среде VBA. Но прежде чем осваивать новые возможности TxtEdit.vbp, изучим то, что создал мастер.

Интеграция VBA базируется на применении библиотеки APC. Мастер же, по сути дела, формирует начальный текст программы для подключения и инициализации основных функций APC в двух созданных им новых модулях классов APCIntegration и Application, присоединенных к исходному проекту. Кроме того, мастер вносит ряд дополнений в существовавшее описание форм. Все программные фрагменты, созданные им, обозначаются логическими скобками в виде комментариев:

?———————————————————-
?**** VBA Integration code, begin insert
...
?**** VBA Integration code, end insert
?———————————————————-

В модуле основной формы frmEdit определяется класс APCIntegration (фактически это объект среды VBA данного приложения):

Public m_apcInt As APCIntegration

затем в процедуре Form_Load создается экземпляр глобального объекта приложения, после чего выполняются необходимые операции инициализации VBA:

Dim appObj As Application
?
Set m_apcInt = New APCIntegration
Set appObj = New Application 
m_apcInt.Initialize appObj, Me.hwnd

Кроме того, в модуль формы добавляются новые дополнительные элементы — меню Tools, команды Macros и Visual Basic Editor, а также текст событийных процедур (см. листинг 1).

Листинг 1

Private Sub mnuVbeMenuItem_Click()
	? Переход в среду VBE
	m_apcInt.showVBE
End Sub
Private Sub mnuMacrosMenuItem_Click()
	? Диалоговое окно Macros будет доступно
	? только после загрузки проекта
	m_apcInt.showMacroDialog
End Sub
Private Sub Form_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. В модуле формы frmEdit также появится новая процедура, созданная мастером (см. листинг 2).

Листинг 2

Private Sub Form_KeyDown(KeyCode As _
	Integer, Shift As Integer)
	? Обработка комбинаций клавиш
	?[Alt][F11]
	If KeyCode = vbKeyF11 And Shift = _
		vbAltMask Then m_apcInt.showVBE
	?[Alt][F8]
	If KeyCode = vbKeyF8 And Shift = _
		vbAltMask Then m_apcInt
.showMacroDialog 
End Sub

Дело в том, что Menu Editor в среде VB не поддерживает использование комбинаций +<клавиша>, поэтому для их обработки требуется создать приведенную выше процедуру.

Модуль класса APCIntegration

Модуль класса APCIntegration имеет значение свойства Instancing=Private и включает внутренние объекты: General, Class, m_apcHost и m_ApcProject. Подпрограммы секции 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 (обращение к этому методу производится из процедуры Form_Load) (см. листинг 3).

Листинг 3

Public Sub Initialize(appObj As _
	Application, hwnd As Long)
	? hWnd (внутренний идентификатор 
	? объектов Windows) для хост-приложения
	m_apcHost.hwnd = hwnd
	?
	? глобальный объект приложения
	m_apcHost.ApplicationObject = appObj
	?
	? имя хост-приложения для ссылки
	? на него из среды VBE
	m_apcHost.HostName = App.Title
	?
	? ключ для лицензирования
	m_apcHost.LicenseKey = _
?16175148714896599659AFABD8ED3C2A416B45E4C
D6F5484BD8CED?
	?
	? ключ Реестра, где будут храниться
	? настройки среды 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 представляет собой параметр, определяющий тип создаваемого приложения:

КонстантаЗначение Действие
AxProjectNormal0Создает стандартный проект
AxProjectHidden1Создает скрытый проект
AxProjectDisableMacros2Создает проект, но отключает все макрокоманды
AxProjectDisableSave4Отключает команду Save в меню File вместе с соответствующей кнопкой панели в среде разработки VBA IDE
AxProjectThrowAway CompiledState8Выбрасывает весь скомпилированный текст из VBA-проекта во время загрузки проекта

Создание VBА-проекта в приложении TxtEdit

Запустим TxtEdit и перейдем в среду VBE (команда Tools|VBE). В окне Project мы увидим содержащийся здесь проект VBAProject, где нет ни одного программного компонента. Командой Tools|Macros создадим макрокоманду (при этом автоматически создастся программный модуль) с названием MyMacro, в которой напишем всего одну строку:

MsgBox ?Моя макрокоманда работает в среде VBE!?

Она доступна через команду Tools|Macros как из VBE, так и из TxtEdit. Но создавать новые макрокоманды из среды TxtEdit можно только при существующем программном модуле в VBAProject. Теперь среда VBE приняла более знакомый вид (рис. 9), поэтому дальнейшие действия таковы: создаем процедуры программного модуля, формы, размещаем на них элементы управления и т. д.

Рис. 9

Сохранение и загрузка VBA-проектов

Макрокоманда MyMacro действительно выполняется, но пока она не делает ничего полезного для редактора TxtEdit. Мы можем написать какой-нибудь программный макрос в VBE, но тогда придется сохранять создаваемый проект, чтобы вносить некоторые коррективы в исходную VB-программу. И тут встретимся с проблемами, в которых нужно разобраться.

Выполним следующие операции:

  1. В среде VBE (File|Save VBAProject) сохраним проект под именем MyVBAPr.vba. Затем вернемся в TxtEdit, запишем в текущий пустой документ фразу «Документ 1» и сохраним его под именем Doc1.rtf. Закроем приложение TxtEdit (т. е. вернемся в среду VB).
  2. Опять запустим TxtEdit и загрузим 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 являются недоступными (рис. 9а).

Рис. 9a

Для решения задачи нужно внести дополнения в текст программы, созданный мастером VBA Integration Wizard. Закроем приложение TxtEdit и вернемся в среду VB. В модуль класса APCIntegration запишем процедуру открытия существующего VBAProject (см. листинг 4).

Листинг 4

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?)

на текст (см. листинг 5).

Листинг 5

Dim msgResult As VbMsgBoxResult
msgResult = MsgBox(?Загрузить ранее
 созданный VBAProject??, _
	vbYesNo, App.Title)
If msgResult = vbYes Then
	? открыть существующий проект
	Call OpenNewVBAProject
End If
If Len(m_stFileName) = 0 Then
	? создать проект по умолчанию
	Set m_ApcProject = _
		m_apcHost.Projects.Add _
	(axProjectNormal, ?VBAProject?)
End If

Итак, вместо создания объекта m_ApcProject по умолчанию мы предлагаем пользователю при запуске приложения выбрать вариант загрузки проекта. Включив в меню TxtEdit специальную команду для загрузки существующего VBAProject, можно одновременно загружать несколько проектов такого типа.

Режимы отладки VBA-проекта

Обновленное приложение TxtEdit теперь может загружать файлы нескольких видов: текстовые документы, а также VBA-проекты типов VBAProject и Project. Связь между средой редактора и VBE может осуществляться путем обращения к макрокомандам VBAProject. Пока мы написали только одну макрокоманду, которая не имеет никакого функционального отношения к TxtEdit, она лишь просто может заявить о том, что действительно получает управление и готова выполнять наши программы. Добавим в нее что-нибудь полезное.

Сейчас мы находимся в среде VB6 с загруженным проектом TxtEdit. Запустим его на выполнение; далее, ответив положительно на вопрос «Загрузить ранее созданный VBAProject?», загрузим MyVBAPr.vba и перейдем в среду VBE.

Рис. 10

Связь макроса в среде VBE с самими офисными программами и их документами в MS Office выполняется через систему объектов. Какие же объекты TxtEdit доступны нам? Для ответа на этот вопрос откроем окно Object Browser (F2) и в списке Project/Library выделим только TxtEdit (рис. 10). Здесь видно, что проект содержит объект Application, имеющий компоненты Application, Parent, Name и VBE. Модернизируем макрокоманду MyMacro так, чтобы прочитать параметр Name, являющийся свойством символьного типа:

MyVar$ = TextEdit.Application.Name
MsgBox ?Имя = ? & MyVar$

Запустив на выполнение MyMacro в текстовом редакторе, мы увидим окно сообщения с названием нашего проекта — TxtEdit. Это уже шаг вперед! Однако в некоторых случаях и здесь можно столкнуться с проблемами.

Создание ActiveX-компонентов

Идея взаимодействия VBA-проектов из среды VBE с нашим исходным приложением становится наконец достаточно понятной: макрос работает с ActiveX-компонентами приложения. И возможности управления приложением из среды VBE полностью зависят от функциональности ActiveX-компонентов.

VBA Integration Wizard создал модуль класса Application, предназначаемый для формирования одноименного объекта, через свойства и методы которого обеспечивается взаимодействие макроса с исходным приложением. Мастер записал туда минимальный текст, пока никак не отражающий специфики конкретной прикладной программы и позволяющий получать лишь самые общие сведения о ней в виде свойств объекта Application (см. листинг 6).

Листинг 6

Public Property Get Application() As Application

	? Возвращает объект приложения,
	? связанный с текущим объектом.
	? В данном случае объект просто
	? возвращает сам себя
	Set Application = Me
End Property
Public Property Get Name() As String
	? Возвращает имя приложения
	Name = frmEdit.m_apcInt.ApcHost.HostName
End Property
Public Property Get Parent() As Application
	? Возвращает прямой родительский
	? объект текущего объекта.
	? В данном случае объект просто
	? возвращает сам себя
	Set Parent = Me
End Property
Public Property Get VBE() As Object
	? Возвращает интерфейс самого
	? высокого уровня для работы с
	? объектной моделью VBE
	Set VBE = frmEdit.m_apcInt.ApcHost.VBE
End Property

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

Делаем очень полезную макрокоманду

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

Листинг 7

Property Get SelColor() As Long
	? Чтение кода цвета выделенного текста
	? в редакторе TxtEdit
	SelColor = frmEdit.RichTextBox1.SelColor
End Property
Property Let SelColor(NewSelColor As Long)
	? Установка цвета выделенного текста
	? в редакторе TxtEdit
	If NewSelColor < 0 Then
		? установка по умолчанию
		frmEdit.RichTextBox1.SelColor =
 RGB(250,
 250, 250)
	Else
		? новое значение
		frmEdit.RichTextBox1.SelColor =
 NewSelColor
	End If
End Property

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

Листинг 8

Sub ModifySelColor()
	? Чтение и коррекция цвета выделенного
	? текста в редакторе TxtEdit
	Dim SelColor&, a$
	SelColor = TxtEdit.Application.SelColor
	a$ = ?Коррекция цвета? & vbCrLf & _
		?выделенного текста.? & vbCrLf
 & vbCrLf & _
		?Введите новое значение
 (16-тиричное)?
	a$ = InputBox(a$, ?Макрокоманда
 ModifySelColor
 среды VBE?, _
		Hex$(SelColor))
	If a$ <> ?? Then ? установка
 нового
 значения цвета текста
		a$ = ?&h? + a$
		TxtEdit.Application.SelColor
 = CLng(a$)
	End If
End Sub

Сохраняем VBAProject (MyVBAPr.vba), закрываем приложение TxtEdit, а затем и среду VB, сохранив исходный текст проекта TxtEdit.vbp.

Поздравляем всех, кто вместе с авторами добрался до этого места в примере! Нам впервые удалось создать собственное приложение с интегрированным в него VBA (рис. 11). Осталось приложить еще немного усилий, чтобы разработать объектную модель и написать соответствующие ActiveX-компоненты, и тогда мы получим текстовый редактор, не уступающий MS Word 2000 по функциональным возможностям и гибкости программирования.

Рис. 11

Подведение итогов

В ходе выполнения примера мы осуществили пять операций.

  1. Провели в среде VB интеграцию VBA в исходное приложение. Это было реализовано в виде создания в VB-проекте двух модулей класса: APCIntegration — инициализация и связь со средой VBE, Application — свойства и методы объекта исходного приложения. Мастер внес также ряд корректив в исходные программные модули проекта.
  2. Вручную внесли дополнения в frmEdit и APCIntegration, чтобы обеспечить возможность загрузки сохраненного ранее VBA-проекта.
  3. Вручную откорректировали frmEdit, чтобы порядок инициализации главной формы проекта стал совместимым с VBE.
  4. Написали простой текст для управления свойством объекта приложения (изменение цвета выделенного текста в документе).
  5. В среде Visual Basic 6.0 запустили исходное приложение TxtEdit.vbp на выполнение. В текстовом редакторе появилась среда VBE, с помощью которой мы написали макрокоманду для управления цветом выделенного текста в документе.

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

В отсутствие VBA SDK нам пришлось бы решать подобную задачу каким-либо из двух способов.

1. Использование среды Visual Basic (или другой подобной программы). В этом случае мы бы действовали так:

  • реализовали исходное VB-приложение в виде ActiveX-сервера (EXE-модуль);
  • загрузили среду Visual Basic и создали в ней проект (аналог макроса), который бы работал с ActiveX-компонентами внешнего приложения.

Однако тогда каждому пользователю придется ставить на компьютер VB, а кроме того, отсутствует связь пользовательского приложения со средой VB (VB не является ActiveX-сервером).

2. Внесение расширений в приложение в виде прямой коррекции его исходного текста. Недостатки данного способа очевидны: такие исправления может делать только один человек — автор программы.

Разумеется, возможность практического использования интеграции VBA в бизнес-приложении упирается в необходимость создания разработчиком соответствующей системы ActiveX-объектов.

И наконец, последнее замечание. Созданное нами приложение TxtEdit работает с исходными файлами двух типов:

  • текстовые документы (TXT и RTF), для обработки которых и предназначено приложение;
  • VBA-проекты, которые загружаются в среде VBE и позволяют расширять функции исходного приложения (созданного в VB 6.0).

Возможен и вариант, реализованный в программах MS Office, когда текст VBA-приложения хранится вместе с обрабатываемым документом. Именно такой подход реализован в приложении SdiNote, входящем в качестве примера в VBA 6.0 SDK 6.1.

С авторами можно связаться по e-mail: orp17@hotmail.com


Как обеспечить совместимость VBA и VB

Если в процессе работы мастера VBA Integration Wizard оставить предлагаемое имя глобального объекта приложения — CApplication — без изменения, то, запустив приложение с интегрированным в него VBA непосредственно в среде VB и перейдя затем в VBA, вы обнаружите, что этот объект недоступен. Для нормальной работы с объектом нужно создать загрузочный EXE-модуль.

Рассмотрим небольшой пример. В среде VB реализуйте самый простой проект — Standard EXE. При этом получите работоспособное приложение, которое только загружает формы. Далее запустите VBA Integration Wizard из меню Add-Ins и пройдите все этапы его работы, просто нажимая кнопку Next (ничего не меняя в полях окон). Таким образом, за полминуты создано приложение, имеющее среду VBA, где можно разрабатывать и выполнять автономные VBA-проекты любой сложности.

1. Запустите приложение Project1, перейдите в среду VBA (-) и в окне Immediate напишите:

Print Project1.CApplication.Name

Нажав , вы увидите сообщение: «Method or data member not found».

2. Теперь сделайте EXE-модуль проекта и повторите операции пункта 1. Ошибки не будет, и в окне Immediate обнаружим имя проекта — Project1.

Получается, что отлаживать макрокоманды, работающие с объектами основного приложения, можно только в EXE-модуле. Это очень неудобно, особенно когда ведутся разработка и отладка таких объектов. Однако существует простое решение проблемы. Нужно переименовать объект CApplication в Application, сделав также соответствующие изменения повсюду, где упоминается данный объект, например с помощью команды Replace. (Еще проще выполнить это в процессе работы мастера VBA Integration Wizard, исправив предлагаемое имя глобального объекта приложения.)

И тогда объект Project1.Application будет доступен и в среде VB, и при работе с EXE-модулем.


Ошибка, возникающая при интеграции VBA в приложения с MDI-интерфейсом

При выполнении примера с многооконным текстовым редактором (интерфейс типа Multiple Document Interface (MDI) — проект MDINote.vbp из статьи «Windows-приложение за пять минут», «Мир ПК», № 8, с. 80) вы увидите странное сообщение об ошибке в операторе макрокода: «Нельзя создать вторую MDI-форму».

Как выяснилось, проблема заключается в тексте программы, сформированном мастером Application Wizard. Оказывается, что конструкция инициализации формы

Public fMainForm As frmMain
Sub Main()
	Set fMainForm = New frmMain
	fMainForm.Show
End Sub

вступает в противоречие с текстом, полученным в результате работы мастера VBA Integration Wizard в модуле Application:

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 во всех модулях проекта.