Андрей Колесов

С появлением Microsoft Office 97 программисты, работающие с VB, оказались в странном положении - в офисных приложениях для настройки интерфейса и автоматизации самых разнообразных операций стал широко использоваться знакомый им VBA, но их собственный инструмент был лишен этой возможности. Точнее, для функционального расширения среды VB (а также VBA в офисных пакетах) можно было применять подключение надстроек COM Add-Ins, реализованных в виде DLL, но в ней никогда ранее не было механизма макросов.

Создание же COM Add-Ins - вовсе не тривиальная задача, хотя бы потому, что при этом нельзя было пользоваться отладочным инструментарием среды разработки (при создании COM Add-Ins для офисных приложений широко используются все те же макросы - сначала проект отлаживается в среде VBA, а потом уже к нему подключается специальный программный интерфейс и проект преобразуется в DLL). Получалось, что даже начинающий пользователь, работающий, например, с Word, мог легко автоматизировать выполнение часто встречающейся операции с помощью функции автоматической записи макроса - Macro Recorder, но в то же время даже опытному VB-программисту сделать это было достаточно сложно (в силу трудоемкости создания COM Add-Ins соответствующие средства расширения разрабатывались обычно в коммерческих целях специализированными фирмами).

С появлением Visual Studio .NET ситуация исправилась - в инструментарии появился механизм разработки и использования макросов. В целом его реализация похожа на то, что многим программистам знакомо по приложениям Microsoft Office, хотя и есть некоторые отличия, на которые мы обратим внимание по ходу статьи*.


*Многие материалы по теме "разработка в среде Microsoft Office", опубликованные, в частности, на сайте http://www.microsoft.ru/offext/officedev, будут полезны для изучения технологии написания макросов для VS.NET. Как это ни парадоксально, но даже опытным разработчикам, которые впервые столкнулись с темой автоматизации своей работы и настройкой IDE, лучше сперва изучить раздел "Для начинающих". Стоит также обратить внимание на статьи в разделе "Создание комплексных решений", где обсуждаются вопросы программного управления проектами.

Объектная модель среды разработки (IDE) базируется на корневом объекте Development Tools Extensibility (DTE), который находится в пространстве имен EnvDTE библиотек классов .NET Framework. Через него можно получить ссылки на все множество объектов, соответствующих отдельным элементам IDE, таким, как Windows, Documents, Solutions, Projects, Debugger и Events (рис. 1). Каждый из этих объектов позволяет обратиться в собственной иерархической системе объектов, коллекций, свойств, методов и событий. Многие из этих компонентов имеют перекрестные связи, поэтому доступ к тому или иному объекту можно получить несколькими путями. Эта система объектов позволяет обращаться практически к любым компонентам и функциям среды разработки VS.NET.

Fig.1
Рис. 1. Общая структура иерархической системы объектов среды разработки. Здесь представлена только небольшая часть основных объектов.

Подчеркнем, что вся объектная модель IDE - сугубо внутренняя. DTE - не COM-объект, он недоступен из внешних приложений, т. е. VS.NET (в отличие от, например, Word) не является ActiveX-сервером. Отметим также, что макросы можно писать только на VB.NET (для создания COM Add-Ins можно использовать любые языки, поддерживающие архитектуру COM). Таким образом получается, что освоить VB желательно всем, кто хочет автоматизировать свою работу в среде VS.NET с помощью макросов.

Знакомство с Macro Explorer

В числе прочих окон управления средой разработки VS.NET предлагает Macro Explorer. Открыть его можно с помощью команды меню Tools|Macros|Macro Explorer или нажатием Alt+F8. Это окно позволяет редактировать, запускать, удалять и создавать макросы. Общая последовательность работы соответствует иерархии компонентов - проект, модуль, макрос.

Открыв окно Macro Explorer (оно добавляется к окну Solution Explorer в виде вкладки), вы увидите три Macro-проекта (рис. 2), которые первоначально загружаются по умолчанию из каталога My Documents\Visual Studio Projects\VSMacros. Впоследствии вы можете создавать новые проекты, добавлять существующие, выгружать ненужные проекты, которые могут храниться в любом месте. Но обратите внимание - все установки управления проектами (например, список загруженных Macro-проектов) связаны с IDE, а не с отдельным VS-решением. В этой связи можно провести аналогию с Microsoft Office, в частности, с Word: в большинстве офисных приложениях Macro-проекты связаны с конкретными документами или шаблонами, являясь их составной частью. В VS.NET Macro-проекты хранятся в отдельных файлах двоичного формата с расширением VSMACROS.

Fig.2
Рис. 2. Структура Macro-проектов, подключенных к среде разработки VS.NET IDE.

Два открывшихся по умолчанию проекта, MacroProject1 и MyMacro, пустые, а проект Samples содержит пять модулей, каждый из которых включает набор макросов. Всего здесь несколько десятков макросов, которые можно использовать для изучения и практического применения. Каждый модуль содержит функции работы с отдельными объектами среды разработки (VSEditor - редактор кода, VSDebugger - отладчик и т. д.).

Чтобы познакомиться с работой макросов, выполните в среде VS.NET следующую процедуру:

  1. Создайте новый Windows-проект (мы будем работать далее с VB.NET).
  2. Откройте окно кода формы и установите курсор в начало процедуры Load.
  3. В окне Macro Explorer раскройте узел модуля VSEditor и последовательно запустите на выполнение (двойным щелчком мыши или сначала щелкнув название правой кнопкой, а потом выбрав Run) макросы NewCommentLine и InsertDateTime (их код приведен в листинге 1).
После этого в код программы у вас добавится строка:

' 26 Октябрь 2002 г. 18:07:25

Чтобы запускать макросы, совсем необязательно открывать окно Macro Explorer - для часто используемых операций можно установить ссылки в виде команд меню, кнопок панелей управления или "горячих" клавиш с помощью диалогового окна Tools|Customize (рис. 3). Настройка пользовательского интерфейса среды разработки VS.NET выполняется точно так же, как в офисных приложениях Microsoft.

Fig.3
Рис. 3. Настройка интерфейса VS.NET IDE с помощью окна Customize.

Среда разработки макросов

Для разработки макросов в VS.NET имеется специальная среда, которая открывается в отдельном окне с помощью команды Macros IDE меню Tools|Macros или при выполнении любой попытки редактирования макроса (рис. 4). Macros IDE - это та же основная среда VS IDE, но с усеченным набором функций. Посмотрим внимательнее на состав Macro-проекта (но сильно углубляться в детали все же не будем, так как это весьма многогранная тема).

Fig.4
Рис. 4. Среда разработки макросов - Macros IDE.

В окне Macro Explorer на рис. 2 в проекте Samples мы видели пять макромодулей. В среде Macro IDE в окнах Project Explorer и Class View (рис. 4) мы видим еще два дополнительных компонента - References (ссылки на внешние объекты, в том числе .NET Framework) и модуль обработки событий EnvironmentEvents. Последний содержит несколько групп событийных процедур, которые позволяют реагировать на операции, выполняемые в основной среде VS IDE. Например, группа DocumentEvents включает события DocumentOpening, DocumentClosing, DocumentClosed и DocumentSaved.

Мы можем расширить список внешних ссылок проекта, выполнив команду Add Reference. Появится знакомое диалоговое окно (рис. 5), но состав доступных компонентов здесь иной по сравнению с VS.NET. В частности, многие объекты .NET Framework тут отсутствуют (не говоря уже о COM-объектах), но появились некоторые другие специальные библиотеки классов.

Fig.5
Рис. 5. Подключение ссылок на библиотеки внешних объектов.

Стоит также обратить внимание на окно Object Browser (см. рис. 3). В списке объектов тут находятся не только ссылки, определенные в узле Reference, но и объекты (макросы, функции) загруженных Macro-проектов и системных библиотек классов. Здесь же можно получить детальную информацию об объектной модели каждой библиотеки, в частности EnvDTE.

В макросе NewCommentLine имеется обращение к функции LineOrientedCommentStart модуля Utilities этого же проекта. Но посмотрите - эта процедура присутствует в окне Class View среды Macro IDE, но отсутствует в Macro Explorer. Объясняется это очень просто: по определению макросом является только процедура типа Sub, в которой отсутствуют передаваемые аргументы. Соответственно, в Macro Explorer видны только макросы, а в Macro IDE - все модули и процедуры проекта.

Из каких элементов может состоять Macro-проект? Кроме обычных программных модулей в него могут также входить модули класса. Никаких визуальных компонентов, например, форм, такой проект содержать не может, в этом плане его возможности существенно слабее, чем у VBA-проектов в офисных приложениях и у COM Add-Ins для VS.NET.

Создание макросов

Самый простой метод создания макросов - использование метода записи. Режим записи временного макроса устанавливается командой Tools|Macros|Record Temporary Macro или нажатием Ctrl+Shift+R. При этом в Macro-проекте, который помечен как Set as Recording Project, создается новый модуль RecordingModule и макрос с именем TemporaryMacro. На экране появляется также панель инструментов Record.

Создадим макрос, который будет создавать в активном окне кода новый метод. Для этого введите с клавиатуры Public Sub MyMethod, а потом нажмите Enter. Закончим запись макроса, нажав на панели Record кнопку Stop Recording. В результате сформируется следующий код макроса:

Sub TemporaryMacro()
  DTE.ActiveDocument.Selection.LineDown()
  DTE.ActiveDocument.Selection.EndOfLine()
  DTE.ActiveDocument.Selection.NewLine()
  DTE.ActiveDocument.Selection.Text = "Public Sub MyMethod"
  DTE.ActiveDocument.Selection.NewLine()
End Sub

Имейте в виду, что запись всегда производится в макрос с фиксированным именем TemporaryMacro, хотя вы можете менять проекты, куда идет запись. Поэтому при начале новой записи макроса старое содержимое TemporaryMacro автоматически теряется без предупреждения. Поэтому сразу после окончания записи макроса и проверки его работоспособности лучше сразу его переименовать - это можно сделать как в Macro Explorer, так и в среде Macro IDE.

Запустите макрос TemporaryMacro, чтобы убедиться в его работоспособности. Затем немного модифицируйте "руками" код созданного макроса, чтобы он мог формировать процедуры с любым именем:

Sub CreateVBSub()
  Dim procname As String
  procname = InputBox("Введите имя процедуры:")
  If Len(procname) > 0 Then
    DTE.ActiveDocument.Selection.LineDown()
    DTE.ActiveDocument.Selection.EndOfLine()
    DTE.ActiveDocument.Selection.NewLine()
    DTE.ActiveDocument.Selection.Text _
      = "Public Sub MyMethod"
    DTE.ActiveDocument.Selection.NewLine()
  End If
End Sub

На этом мы закончим наше первое знакомство с использованием макросов для автоматизации операций в среде VS.NET. В целом широкие возможности дополнительной среды Macro IDE представляются очень интересными и полезными, но для их освоения потребуется немало времени, так как описание среды в документации очень скудное. Приходится использовать тривиальный, но трудоемкий метод "проб и ошибок". Поэтому я подведу некоторые промежуточные итоги своих изысканий.

Использование иерархической модели объектов DTE предоставляет очень широкие возможности управления средой VS.NET IDE. Но освоение среды - в силу обилия ее компонентов - потребует времени. На первом этапе хорошим подспорьем будут примеры, содержащиеся в Macro-проекте Samples. Весьма перспективным представляется использование событийных предопределенных макросов модуля EnvironmentEvents, которые позволяют автоматически выполнять нужные операции в процессе разработки решений в среде VS.NET IDE.

В целом разработка макросов в VS.NET похожа на аналогичную процедуру в Microsoft Office. Но очевидно, что пока возможности VS.NET при создании сложных макросов заметно слабее. Например, сейчас здесь нет возможности применения форм, элементов управления, внешних COM-объектов и т. д. Среда Macro IDE не является контейнером ActiveX, из нее нельзя, например, обращаться к тем же офисным приложениям.

Метод создания макросов с помощью Macro Recorder очень прост, но, к сожалению, некоторые мои макросы, созданные таким образом, не хотели работать: выдавались сообщения о неверном использовании команд, о конфликтах потоков и т. п. Возможно, для их устранения нужно более точное управление установками среды, но вполне вероятно, что есть какие-то дефекты самого VS.NET (не будем забывать - ведь это версия 1.0!).

Применение COM Add-Ins

Как уже отмечалось в начале статьи, для автоматизации работы в среде VS.NET IDE можно использовать расширения в виде COM Add-Ins. Их создание выглядит несколько более сложным по сравнению с созданием макросов (прежде всего потому, что разделены процессы написания кода и отладки), но возможности COM Add-Ins гораздо шире - это могут быть полноценные приложения с развитым пользовательским интерфейсом, с применением баз данных и всей функциональности .NET Framework. COM Add-Ins могут подключаться не только к среде VS.NET IDE, но и к Macro IDE. Механизм их создания фактически тот же самый, что использовался в VS 6.0, VB 6.0, VBA 6.0 и Microsoft Office, на базе подключения программного интерфейса IDTExtensibility2. Эти COM-компоненты можно писать, вообще говоря, на любом языке, поддерживающем COM, но специальные средства разработки COM Add-Ins в VS.NET позволяют работать только с VB.NET и C#.

Для создания COM Add-Ins в VS.NET нужно запустить мастер Add-Ins Project Wizard, который находится в папке Other Projects|Extensibility Projects диалогового окна New Project. Собственно, в этой папке два мастера - для создания расширения специально для VS.NET и совместное (Shared) дополнение, которое можно подключать к различным приложениям (в том числе Microsoft Office). Подробнее тему разработки COM Add-Ins в среде VS.NET мы рассмотрим в одной из последующих публикаций.

Листинг 1. Два макроса из состава проекта Samples

Imports EnvDTE

Public Module VSEditor
 Sub NewCommentLine()
  ' вставляет в окно текущего документа
  ' строку, которую помечает 
  ' как "комментарий"
  Dim ts As TextSelection = _
    DTE.ActiveWindow.Selection
  ts.NewLine()
  ' определяет знак "комментария" для 
  ' текущего языка программирования 
  ts.Insert _
    (Utilities.LineOrientedCommentStart())
  ts.Insert(" ")
 End Sub

 Sub InsertTimeDate()
  ' вставляет в окно текущего документа 
  ' значение текущей даты и времени   
  Dim objTextSelection As TextSelection
  objTextSelection = _
    CType(DTE.ActiveDocument.Selection(), _
    EnvDTE.TextSelection)
  objTextSelection.Text = _
    System.DateTime.Now.ToLongDateString() _
     + " " + _
    System.DateTime.Now.ToLongTimeString()
 End Sub
End Module