
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
Г Л А В А
9
Расширение возможностей интегрированной среды разработки
Visual Studio .NET
Эффективные средства разработки должны быть достаточно гибки, чтобы ра! ботать на вас, а не заставлять приспосабливаясь к ним. По!моему, до Microsoft Visual Studio .NET предыдущие версии Visual Studio были достаточно хороши только в плане отладки, но не редактирования программ и общего использования. Теперь же я с радостью могу сообщить, что, хотя Visual Studio .NET все еще не лишена недостатков, именно эту среду я использую для редактирования и разработки. В первую очередь это объясняется тем, что в сравнении с предыдущими версия! ми Visual Studio среда Visual Studio .NET — просто образец расширяемости. Если вы считаете, что какие!то функции в ней реализованы не так или просто упущены, то найдете все средства, необходимые для исправления подобных недостатков.
Когда я работал в NuMega, нас очень часто спрашивали, как встроить собствен! ные окна в Visual Studio. Сейчас я с легкостью могу ответить на этот вопрос, но это уже не важно, поскольку это теперь доступно каждому. Почти каждый разра! ботчик хотел бы встроить в IDE то или иное средство или окно, но до сих пор у нас не было удобного способа сделать это. Новая модель расширяемости Visual Studio .NET позволяет теперь любому программисту добавить в IDE собственные окна. Способы этого описаны и поддерживаются гораздо лучше, чем специаль! ная Package Partner Program, необходимая для внедрения в IDE в предыдущих вер! сиях Visual Studio.
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
373 |
|
|
Расширяемость IDE складывается из макросов, надстроек и мастеров. Для со! здания макросов в Visual Studio .NET служит встроенный редактор макросов (Macros IDE editor). Он выглядит и ведет себя точно так же, как и среда Visual Studio .NET, поэтому ваши усилия по изучению этой среды сполна окупятся при создании макросов. Стоит сказать, что макросы имеют одно ограничение: разрабатывать их можно только на Microsoft Visual Basic .NET. Предполагалось, что .NET будет обес! печивать полную гибкость в использовании языков программирования, поэтому я не могу понять, почему Microsoft пошла на это ограничение, не поддержав C#. По сути это значит, что, даже если вы считаете себя самым преданным поклон! ником C# (возможно, поскольку вам очень нравятся точки с запятой), для созда! ния макросов все же придется освоить и Visual Basic .NET.
Второй вариант расширения IDE обеспечивают надстройки. Макросы отлич! но подходят для небольших задач, не связанных с пользовательским интерфей! сом, тогда как надстройки — это компоненты COM, позволяющие создавать ис! тинные расширения IDE. Например, надстройки позволяют создавать окна инст! рументов (свои собственные), добавлять страницы свойств в диалоговое окно Options (свойства) и реагировать из надстроек на команды меню. Любой програм! мист, которому вы предоставите свой макрос, сможет изучить его исходный код, тогда как надстройки распространяются в двоичной форме и для их создания подойдет любой язык, поддерживающий COM.
Наконец, для расширения функциональности IDE служат мастера. Они особенно полезны, когда для решения какой!либо задачи пользователь должен выполнить ряд действий. Отличный пример — мастер Smart Device Application Wizard, помо! гающий создать приложение для «интеллектуального» устройства. Из всех спосо! бов расширения IDE мастера используются реже всего.
В этой главе я хочу показать вам возможности макросов и надстроек, рассмотрев три реальных средства, без которых я уже просто не могу жить. Увидев, на что они способны, вы получите неплохое представление о проблемах, с которыми можно столкнуться, создавая собственное Средство, Без Которого Никто Не Мо! жет Жить. Так как очень немногие программисты нуждаются в создании масте! ров, я не буду обсуждать эту тему. Что до макросов и надстроек, то в отличие от других авторов я не буду опускаться до подробностей типа «чтобы появилась надстройка, нажмите на эту кнопку мастера». Я полагаю, что вы изучили докумен! тацию к Visual Studio .NET, поэтому, чтобы вы могли сэкономить время при со! здании своих средств, основное внимание я уделю проблемам, с которыми в свое время столкнулся сам.
Первое описываемое мной средство — CommenTater — очень полезный мак! рос, гарантирующий наличие и актуальность комментариев в программах C#. Первая из двух надстроек, SuperSaver, исправляет один недостаток сохранения файлов в Visual Studio .NET и реализует функции фонового сохранения файлов и добавления страниц свойств в окно Options. Этот отличный пример полной над! стройки понравится вам еще и небольшим объемом. Вторая надстройка, Settings! Master, позволяет автоматизировать конфигурирование параметров сборки про! граммы, чтобы безо всяких усилий вы могли использовать во всех проектах па! раметры, рекомендованные мной в главе 2. Благодаря SettingsMaster координиро! вать все проекты группы будет проще простого. Вам больше не придется вруч! ную изменять параметры своих проектов! Все эти средства вы найдете на CD.

374 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Расширение IDE при помощи макросов
Сначала я хотел бы рассмотреть некоторые проблемы, связанные с макросами. Прежде всего замечу, что, даже если вам кажется, что вы придумали самую луч! шую надстройку в мире и вам не терпится приступить к ее реализации, не спе! шите. Так как в макросах используются те же объекты и свойства, что и в надстрой! ках, макросы обеспечивают наилучшую возможность изучить подробности объек! тной модели Visual Studio .NET. Как вы увидите, объектная модель имеет ряд хит! ростей, поэтому создание надстроек иногда вызывает проблемы. Макросы гораз! до проще создавать и отлаживать, поэтому я рекомендую для начала разобраться с ними.
Не торопитесь открывать меню Tools (инструменты) и выбирать пункт Macros (макросы) — почитайте документацию о макросах и объектной модели. Макро! сы описываются в разделе Visual Studio .NET\Developing With Visual Studio .NET\Mani! pulating The Development Environment\Automating Repetitive Actions By Using Macros, объектная модель — в разделе Visual Studio .NET\Developing With Visual Studio
.NET\Reference\Automation And Extensibility Reference.
Рис. 9 1. Окно Macro Explorer
Изучив объекты, попробуйте записать какие!нибудь макросы, чтобы увидеть объекты в действии. Помните: запись макросов работает преимущественно в ре! дакторах кода [включая диалоговые окна Find/Replace (найти/заменить)], при использовании Solution Explorer (проводник для работы с решением) и при ак! тивизации окон. Вы не сможете записать такие действия, как создание формы Web или Windows с элементами управления. Изучите предоставленные Microsoft при! меры макросов, которые автоматически загружаются в Macro Explorer (провод! ник для работы с макросами) (рис. 9!1) при загрузке проекта макросов Samples. Примеры макросов наглядно иллюстрируют использование объектной модели для решения проблем. Больше всего мне нравится макрос MakeAddinFromMacroProj (из проекта MakeAddin), преобразующий макрос в надстройку. Он демонстриру! ет всю мощь, которую нам дает Visual Studio .NET.

ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
375 |
|
|
Выполнить макрос можно двумя способами: дважды щелкнуть имя функции макроса в Macro Explorer или вызвать окно Command (команды). Если вы начне! те писать в окне Command Window слово «macro», технология IntelliSense отобразит окно подсказки, в котором можно выбрать нужный макрос (рис. 9!2).
Рис. 9 2. Выполнение макроса из окна Command, поддерживающего технологию IntelliSense
Если вы предпочитаете выполнять макросы или встроенные команды из окна Command, можете назначить им более короткие текстовые имена при помощи встроенной команды alias. Это избавит вас от необходимости писать при каж! дом запуске макроса что!нибудь вроде:
Macros.BugslayerMacros.CommenTater.AddNoCommentTasksForSolution
Для удаления псевдонима служит команда alias с ключом /d.
Параметры макросов
Macro Explorer и всплывающая подсказка IntelliSense в окне Command имеют одну не всегда очевидную особенность: в обоих окнах отображаются только макросы, не принимающие параметров. Это имеет смысл в Macro Explorer, так как иначе было бы очень сложно передать макросу параметры при двойном щелчке его имени. Однако при использовании окна Command возможность передачи пара! метров оказалась бы совсем не лишней. Для этого в объявлении макроса нужно указать, что он принимает единственный необязательный (optional) строковый параметр:
Sub ParamMacro(Optional ByVal Param As String = "")
В случае нескольких параметров все почти так же просто: для каждого из них нужно добавить по одному необязательному строковому параметру. Вот пример макроса, принимающего три параметра:
Sub ParamMacroWithThree(Optional ByVal Param1 As String = "", _
Optional ByVal Param2 As String = "", _
Optional ByVal Param3 As String = "")
Использование нескольких необязательных строковых параметров отлично работает в Visual Studio .NET 2003, но если вы хотите, чтобы ваши макросы были обратно совместимы с Visual Studio .NET 2002, то будете разочарованы: такие макросы не запускаются даже в окне Command. В Visual Studio .NET можно ука! зать только один необязательный строковый параметр. Если же вы хотите пере! давать несколько параметров в окне Command, ситуация становится несколько странной. При наличии пробелов макрос не вызывается. Это значит, что, если вам

376 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
нужно передать в макрос три параметра, передавайте их в одной строке, разде! ляя запятыми. Если ваш строковый параметр содержит пробелы, его нужно пере! давать отдельной строкой, заключив в кавычки. Странно, но в фактической стро! ке, обрабатываемой вашим макросом, кавычек не будет. Замечу, что передать зак! люченную в кавычки строку, содержащую запятую, невозможно. Вот два примера правильной передачи строк в макросы с соответствующими значениями строк при обработке параметров макроса:
Вызов макроса из окна Command: MyMacro x,y,z Строка параметров макроса: x,y,z
Вызов макроса из окна Command: MyMacro x,"a string",y Строка параметров макроса: x,a string,y
Чтобы упростить работу с макросами в обеих версиях Visual Studio, я включил
вчисло файлов к этой книге вспомогательный модуль (Utilities.VB, находящийся
вкаталоге Macros), который содержит функцию SplitParams, разделяющую пара! метры и заносящую их в массив строк. В этот же модуль я включил некоторые удобные оболочки для объектов окон Command и Output (вывод). Мы использу! ем эти объекты, когда будем работать с макросом SimpleMacros (см. раздел «Эле! менты кода»).
Проблемы с проектами
Здесь я должен сделать одно важное замечание: при чтении документации не совсем ясно, что в разных языках используются разные объектные модели проектов. В общем обсуждении объектной модели среды для объекта Project приводится об! ширный список полезных методов для манипуляции проектами и их сохранения. Из!за ошибочного впечатления, что общий проект является корнем всех проек! тов, разрабатываемых с использованием всех языков и технологий, мне пришлось потратить впустую много времени. На самом деле нет ничего, что находилось бы так далеко от истины. Есть только два правильно документированных типа про! ектов: VSProject для проектов C# и Visual Basic .NET и VCProject для проектов C++. Другие типы проектов, такие как CAB и Setup, не документированы и будут гене! рировать массу исключений, если вы будете получать к ним доступ через общий объект Project. Если методы, которые, по вашему мнению, должны работать, та! кие как Save, генерируют исключения Not Implemented (метод не реализован), это очень раздражает. Работая с проектами, убедитесь, что вы реализовали достаточ! но обработчиков исключений!
При перечислении проектов решения вам предоставляется общий объект Project. Для определения типа проекта лучше всего использовать GUID, который позво! ляет получить свойство Kind. Строки GUID для разных типов проектов см. в табл. 9!1. Если вы разрабатываете проект на C++, работоспособными будут лишь немногие из методов Project, поэтому немедленно преобразуйте Project в VCProject через свойство Object и используйте объект VCProject. Объекты VSProject относятся к доступу к ним через общий объект Project чуть благожелательнее.
Альтернативный метод определения типа проекта — использование свойства Project.CodeModel, описывающего элементы кода в файлах проекта. Об элементах кода я расскажу чуть ниже. Объект CodeModel содержит свойство Language, которое

ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
377 |
|
|
возвращает строку GUID, определяющую язык. В надстройке SettingsMaster я опре! делил тип проекта с помощью этого метода, так как мне нужно было поддержи! вать все типы языков. Кстати, в документации допущена ошибка: строки, возвра! щаемые свойством Language, не являются константами vsCMLanguage — такой кон! станты нет. На самом деле это свойство возвращает константу CodeModelLanguage Constants.
Табл. 9-1. Документированные строки GUID для разных типов проектов
Язык проекта |
GUID |
C# |
{FAE04EC0!301F!11D3!BF4B!00C04F79EFBC} |
Visual Basic .NET |
{F184B08F!C81C!45F6!A57F!5ABD9991F28F} |
C++ |
{8BC9CEB8!8B4A!11D0!8D11!00A0C91BC942} |
J# |
{E6FDF86B!F3D1!11D4!8576!0002A516ECE8} |
|
|
Элементы кода
Одно из самых удивительных свойств Visual Studio .NET в том, что через объект! ную модель можно легко получить доступ ко всем конструкциям программиро! вания в исходных файлах. То, что теперь мы можем безо всякого синтаксическо! го анализа добавлять/изменять/удалять, скажем, методы в программах на всех язы! ках, поддерживаемых Visual Studio .NET, открывает широкие возможности по со! зданию уникальных средств, которые могли бы никогда не быть созданы из!за чрезвычайной сложности синтаксического анализа. Каждый раз, когда я исполь! зую элементы кода для изменения кода в исходных файлах, я удивляюсь тому, насколько прекрасна эта возможность.
Чтобы показать, насколько легко использовать элементы кода, я написал мак! рос, который создает дамп элементов кода активного документа (листинг 9!1). Из листинга не видно, что данный код работает для любого языка. В выводе указыва! ется имя элемента кода вместе с его типом. Этот макрос хранится в файле Mac! ros\SimpleMacros.VB на CD; макросу нужен также вспомогательный файл Utilities.VB. Вот частичный результат обработки этим макросом файла SETTINGSMASTER.VB из проекта SettingsMaster:
SettingsMaster.SettingsMaster(vsCMElementClass)
SettingsMaster.SettingsMaster.RegSettings(vsCMElementVariable)
SettingsMaster.SettingsMaster.m_ApplicationObject(vsCMElementVariable)
SettingsMaster.SettingsMaster.New(vsCMElementFunction)
ApplicationObject(vsCMElementParameter)
AddInInstance(vsCMElementParameter)
При работе с элементами кода возникает небольшая проблема с согласован! ностью получения дочерних элементов. Например, объект CodeClass получает подэлементы классов Visual Basic .NET или C# через свойство Members. Однако, чтобы получить подэлементы классов C++, описываемых объектом CodeClass, служит свой! ство Children. В заключительной части процедуры DumpElements я использовал для нахождения нужных дочерних элементов несколько вложенных блоков Try…Catch. Как можно увидеть, для получения дочерних объектов объекты CodeFunction ис! пользуют еще одно свойство — Parameters. Важно, чтобы вы знали о разных спо! собах получения дочерних элементов.

378 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Листинг 9-1. Процедура DumpActiveDocCodeElements
' Создание дампа всех элементов кода для открытого документа проекта.
Public Sub DumpActiveDocCodeElements()
'Создание объекта для вывода информации. Заметьте:
'класс OutputPane относится к проекту макросов Utilities.
Dim ow As OutputPane = New OutputPane("Open Doc Code Elements")
'Очистка панели вывода. ow.Clear()
'Есть ли открытый документ?
Dim Doc As Document = DTE.ActiveDocument
If (Doc Is Nothing) Then
ow.WriteLine("No open document") Exit Sub
End If
'Получение модели кода для этого документа. Для работы
'с элементами кода надо использовать элемент проекта.
Dim FileMod As FileCodeModel = Doc.ProjectItem.FileCodeModel
If (Not (FileMod Is Nothing)) Then
DumpElements(ow, FileMod.CodeElements, 0)
Else
ow.WriteLine("Unable to get the FileCodeModel!") End If
End Sub
Private |
Sub DumpElements(ByVal ow |
As OutputPane, _ |
|||
|
|
ByVal |
Elems |
As |
CodeElements, _ |
|
|
ByVal |
Level |
As |
Integer) |
Dim |
Elem |
As CodeElement |
|
|
|
For |
Each |
Elem In Elems |
|
|
|
Dim i As Integer = 0
While (i < Level) ow.OutPane.OutputString(" ") i = i + 1
End While
'Если при получении доступа к свойству FullName генерируется
'исключение, вероятно, это объясняется безымянным параметром. Dim sName As String
Try
sName = Elem.FullName Catch e As System.Exception
sName = "'Empty Name'" End Try
ow.WriteLine(sName + "(" + Elem.Kind.ToString() + ")")

ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
379 |
|
|
'Это довольно странно. Некоторые объекты CodeElements используют
'для получения подэлементов свойство Children, тогда как другие —
'свойство Members. Ну, а функции используют свойство Parameters. Dim SubCodeElems As CodeElements = Nothing
Try
SubCodeElems = Elem.Children
Catch
Try
SubCodeElems = Elem.Members
Catch
If (TypeOf Elem Is CodeFunction) Then
SubCodeElems = Elem.Parameters
Else
SubCodeElems = Nothing
End If
End Try
End Try
If (Not (SubCodeElems Is Nothing)) Then
If (SubCodeElems.Count > 0) Then
DumpElements(ow, SubCodeElems, Level + 1)
End If
End If
Next
End Sub
CommenTater: лекарство
от распространенных проблем?
Одним из абсолютно бесценных свойств C# являются документирующие коммен! тарии XML. Так называют тэги XML, содержащиеся в комментариях, описывающих свойства или методы в конкретном файле. Фактически IDE помогает вам, автома! тически включая такие комментарии для конструкций программы, перед которыми вы пишете ///. Есть три очень веских причины, почему всегда следует заполнять документирующие комментарии C#. Во!первых, это стандартизирует коммента! рии между отдельными группами и во всей вселенной C#. Во!вторых, технология IntelliSense среды разработки автоматически отображает информацию, указанную в тэгах <summary> и <param>, что облегчает использование вашего кода другими программистами, предоставляя им гораздо больше данных об элементах вашей программы. Если код является частью проекта, для получения преимуществ доку! ментирующих комментариев ничего делать не нужно. Если вы предоставляете решение только в двоичной форме, документирующие комментарии могут быть собраны в XML!файл при компиляции, поэтому и в такой ситуации вы можете предоставить пользователям отличный набор подсказок. Для этого нужно только разместить итоговый XML!файл в том же каталоге, что и двоичный файл, и Visual Studio .NET будет автоматически отображать комментарии в подсказках IntelliSense.

380 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Наконец, при помощи XSLT из итогового XML!файла можно создать полную систему документации к вашей программе. Заметьте, что я не имею в виду коман! ду Build Comment Web Pages (создать Web!страницы комментариев) из меню Tools. Эта команда не учитывает много важной информации, например, тэги <exception>, поэтому она не так уж и полезна. Как я покажу чуть ниже, для генерирования документации можно использовать гораздо лучшие средства.
Чтобы максимально эффективно документировать свой код, изучите в доку! ментации к Visual Studio .NET все, что касается тэгов комментариев XML. Для со! здания файла XML!документа откройте окно Property Pages (страницы свойств), папку Configuration Properties (конфигурационные свойства), страницу Build (сбор! ка программы) и заполните поле XML Documentation File (файл XML!документа! ции) (рис. 9!3). Это поле нужно заполнять отдельно для каждой конфигурации, чтобы файл документации создавался при каждой сборке программы.
Рис. 9 3. Установка ключа командной строки /doc для создания файла документирующих комментариев XML
Чтобы вы могли создать полный вывод из файлов комментариев XML, я раз! местил в каталоге DocCommentsXSL на CD файл трансформации XSL и каскадную таблицу стилей. Однако гораздо лучше использовать средство NDoc, которое можно загрузить по адресу http://ndoc.sourceforge.net. NDoc обрабатывает XML!коммен! тарии и создает файл помощи HTML, который выглядит в точности, как докумен! тация MSDN к библиотеке классов .NET Framework. NDoc даже предоставляет ссылки на общие методы вроде GetHashCode, так что из него вы можете переходить прямо в документацию MSDN! NDoc — прекрасный способ документирования кода ва! шей группы, и я настоятельно рекомендую его использовать. Благодаря реализо! ванной в Visual Studio .NET 2003 обработке программы после ее сборки (post build processing) вы можете с легкостью включить NDoc в свой процесс сборки.
Так как документирующие комментарии настолько важны, мне захотелось разработать метод автоматического их добавления в мой код C#. Примерно в то же время, когда я об этом подумал, я обнаружил, что окно Task List (список зада! ний) автоматически отображает все комментарии, начинающиеся с ключевых фраз вроде «TODO», когда вы нажимаете в нем правую кнопку и выбираете в меню Show Tasks (показать задания) пункт All (все) или Comment (комментарии). Я решил
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
381 |
|
|
создать макрос или надстройку, которые добавляли бы все пропущенные докумен! тирующие комментарии и обрабатывали в них фразу «TODO», чтобы можно было легко просматривать комментарии и гарантировать их правильное заполнение. Результатом стал CommenTater. Вот пример метода, обработанного CommenTater:
///<summary>
///TODO Add Test function summary comment
///</summary>
///<remarks>
///TODO Add Test function remarks comment
///</remarks>
///<param name="x">
///TODO Add x parameter comment
///</param>
///<param name="y">
///TODO Add y parameter comment
///</param>
///<returns>
///TODO Add return comment
///</returns>
public static int Test ( Int32 x , Int32 y )
{
return ( x ) ;
}
Visual Studio .NET делает перебор элементов кода в исходном файле тривиаль! ной задачей, поэтому я был очень доволен, так как думал, что мне нужно будет только просмотреть элементы кода, получить строки, предшествующие любому методу или свойству, и вставить комментарии в случае их отсутствия. Когда я обнаружил, что все элементы кода имеют свойство DocComment, возвращающее дей! ствительный комментарий для данного элемента, я тут же снял шляпу перед раз! работчиками за то, что они продумали все заранее и сделали элементы кода по! настоящему полезными. Теперь мне нужно было только присвоить свойству Doc Comment нужное значение, и все было бы чудесно.
Возможно, сейчас вам следует открыть файл CommenTater.VB из каталога Com! menTater. Исходный код этого макроса слишком объемен, чтобы воспроизводить его в книге, поэтому следите за моими мыслями по файлу. Моя основная идея заключалась в создании двух процедур, AddNoCommentTasksForSolution и CurrentSource FileAddNoCommentTasks. Вы можете по их именам сказать, на каком уровне они ра! ботают. Большей частью базовый алгоритм похож на примеры из листинга 9!1: я просто просматриваю все элементы кода и использую их свойства DocComment.
Первая проблема, с которой я столкнулся, была связана с тем, что я считаю небольшим недостатком объектной модели элементов кода. Свойство DocComment не является общим для класса CodeElement, который может быть использован в качестве базового класса для любого общего элемента кода. Поэтому мне пришлось преобразовать общий объект CodeElement в действительный тип элемента, опира! ясь на свойство Kind. Вот почему процедура RecurseCodeElements содержит большой оператор Select…Case.