
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
382 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Вторая проблема была полностью на моей совести. Я почему!то никогда не осознавал, что со свойством DocComment конструкции кода нужно обращаться, как с полноценным XML!фрагментом. Я создавал нужную строку комментария, но, когда
япытался назначить ее свойству DocComment, генерировалось исключение Argument Exception. Я был очень озадачен этим, так как думал, что свойство DocComment до! пускает чтение и запись, но на деле все выглядело так, будто оно поддерживало только чтение. Из!за какого!то помутнения я не понимал, что генерирование исключения объяснялось тем, что я не заключал документирующие комментарии XML в элементы <doc></doc>. Вместо этого я решил, что столкнулся с непонятной проблемой, и стал искать альтернативные средства включения текста комментария.
Так как отдельные элементы кода имеют свойство StartPoint, мне просто нуж! но было создать соответствующий объект EditPoint и ввести текст. Эксперимен! ты быстро показали, что все работало правильно, и я начал разрабатывать набор процедур для добавления текста. Делать это вручную требуется не так уж и редко, поэтому я закомментировал первоначальные процедуры и оставил в конце фай! ла CommenTater.VB.
Первую версию макроса я часто использовал в своих проектах. Иногда макро! сы могут быть слишком медленными, поэтому я рассматривал возможность пре! образования CommenTater в полноценную надстройку, но меня его скорость все! гда устраивала. Первая версия CommenTater только добавляла пропущенные ком! ментарии. Это было прекрасно, но скоро я понял, что мне по!настоящему хочет! ся, чтобы CommenTater был умнее и сравнивал имеющиеся комментарии к функ! циям с тем, что на самом деле присутствует в коде. При изменении прототипов функций, скажем, при добавлении/удалении параметров, я часто забываю обно! вить соответствующие комментарии. Добавив эту функциональность сравнения,
ясделал бы CommenTater еще полезнее.
Начав думать о том, что потребуется для обновления существующих коммен! тариев, я слегка загрустил. Если вы помните, в тот момент я думал, что свойство DocComment допускает только чтение, поэтому я решил, что для правильного обнов! ления комментариев придется выполнять значительный объем манипуляции с текстом, и это меня не привлекало. Однако, когда я взглянул на CommenTater в отладчике макросов, на меня снизошло радостное озарение, и я понял, что для записи в свойство DocComment нужно просто размещать вокруг каждого коммента! рия элементы <doc></doc>. Когда я преодолел собственную глупость, написать процедуру ProcessFunctionComment оказалось гораздо проще (листинг 9!2).
В этот момент в игру вступила мощь библиотеки классов Microsoft .NET Frame! work. Чтобы выполнить всю трудную работу, нужную для получения информации из существующих строк документирующих комментариев и их преобразования, я использовал прекрасный класс XmlDocument. Процедура ProcessFunctionComment должна была поддерживать переупорядочение комментариев, поэтому я должен был по! добрать порядок размещения отдельных узлов в файле. Хочу отметить, что я фор! матирую комментарии так, как мне нравится, поэтому CommenTater может изме! нить тщательное форматирование ваших комментариев, но никакой информации он не выбросит.

ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
383 |
|
|
Листинг 9-2. Процедура ProcessFunctionComment из файла CommenTater.VB
'Эта процедура получает имеющиеся комментарии к функциям
'и гарантирует, что все в порядке. Она может преобразовывать
'ваши комментарии, поэтому вы можете захотеть изменить ее. Private Sub ProcessFunctionComment(ByVal Func As CodeFunction)
Debug.Assert("" <> Func.DocComment, """"" <> Func.DocComment")
'Объект, содержащий исходный документирующий комментарий. Dim XmlDocOrig As New XmlDocument()
'ЭТО ЗДОРОВО! После присвоения свойству PreserveWhitespace
'значения True класс XmlDocument будет отвечать почти за все,
'что касается форматирования...
XmlDocOrig.PreserveWhitespace = True
XmlDocOrig.LoadXml(Func.DocComment)
Dim RawXML As New StringBuilder()
' Получение узла "summary". Dim Node As XmlNode
Dim Nodes As XmlNodeList = XmlDocOrig.GetElementsByTagName("summary") If (0 = Nodes.Count) Then
RawXML.Append(SimpleSummaryComment(Func.Name, "function"))
Else
RawXML.AppendFormat("<summary>{0}", vbCrLf)
For Each Node In Nodes
RawXML.AppendFormat("{0}{1}", Node.InnerXml, vbCrLf)
Next
RawXML.AppendFormat("</summary>{0}", vbCrLf)
End If
' Получение узла "remarks".
Nodes = XmlDocOrig.GetElementsByTagName("remarks")
If (Nodes.Count > 0) Then
RawXML.AppendFormat("<remarks>{0}", vbCrLf)
For Each Node In Nodes
RawXML.AppendFormat("{0}{1}", Node.InnerXml, vbCrLf)
Next
RawXML.AppendFormat("</remarks>{0}", vbCrLf)
ElseIf (True = m_FuncShowsRemarks) Then
RawXML.AppendFormat("<remarks>{0}TODO Add {1} function " + _ "remarks comment{0}</remarks>", _
vbCrLf, Func.Name)
End If
' Получение всех параметров, описанных в документирующих комментариях. Nodes = XmlDocOrig.GetElementsByTagName("param")
см. след. стр.

384 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
' Имеет ли функция параметры?
If (0 <> Func.Parameters.Count) Then
'Занесение всех существующих параметров комментариев
'в хэш таблицу с именем параметра в качестве ключа. Dim ExistHash As New Hashtable()
For Each Node In Nodes
Dim ParamName As String Dim ParamText As String
ParamName = Node.Attributes("name").InnerXml ParamText = Node.InnerText ExistHash.Add(ParamName, ParamText)
Next
'Просмотр параметров.
Dim Elem As CodeElement
For Each Elem In Func.Parameters
' Есть ли этот элемент в хэше заполненных параметров? If (True = ExistHash.ContainsKey(Elem.Name)) Then
RawXML.AppendFormat("<param name=""{0}"">{1}{2}{1}" + _ "</param>{1}", _
Elem.Name, _
vbCrLf, _
ExistHash(Elem.Name)) ' Удаление этого ключа. ExistHash.Remove(Elem.Name)
Else
' Был добавлен новый параметр.
RawXML.AppendFormat("<param name=""{0}"">{1}TODO Add " + _ "{0} parameter comment{1}</param>{1}", _
Elem.Name, vbCrLf)
End If
Next
'Если в хэш таблице что то осталось, параметр был или удален,
'или переименован. Я добавлю описания оставшихся параметров
'с пометками TODO, чтобы пользователь мог удалить их вручную. If (ExistHash.Count > 0) Then
Dim KeyStr As String
For Each KeyStr In ExistHash.Keys Dim Desc = ExistHash(KeyStr)
RawXML.AppendFormat("<param name=""{0}"">{1}{2}{1}{3}" + _
|
|
"{1}</param>{1}", _ |
KeyStr, |
_ |
|
vbCrLf, |
_ |
|
Desc, |
_ |
|
"TODO |
|
Remove param tag") |
Next
End If
End If
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
385 |
|
|
|
|
|
|
|
' Обработка возвращаемых значений, если таковые имеются. |
|
|
If ("" <> Func.Type.AsFullName) Then |
|
|
Nodes = XmlDocOrig.GetElementsByTagName("returns") |
|
|
' Обработка узлов "returns". |
|
|
If (0 = Nodes.Count) Then |
|
|
|
RawXML.AppendFormat("<returns>{0}TODO Add return comment" + _ |
|
|
"{0}</returns>{0}", _ |
|
|
vbCrLf) |
|
Else |
|
|
|
RawXML.AppendFormat("<returns>{0}", vbCrLf) |
|
|
For Each Node In Nodes |
|
|
RawXML.AppendFormat("{0}{1}", Node.InnerXml, vbCrLf) |
|
|
Next |
|
|
RawXML.AppendFormat("</returns>{0}", vbCrLf) |
|
End |
If |
|
End If |
|
|
' Обработка узлов "example". |
|
|
Nodes = |
XmlDocOrig.GetElementsByTagName("example") |
|
If (Nodes.Count > 0) Then |
|
|
RawXML.AppendFormat("<example>{0}", vbCrLf) |
|
|
For |
Each Node In Nodes |
|
|
RawXML.AppendFormat("{0}{1", Node.InnerXml, vbCrLf) |
|
Next |
|
|
RawXML.AppendFormat("</example>{0}", vbCrLf) |
|
|
End If |
|
|
' Обработка узлов "permission". |
|
|
Nodes = |
XmlDocOrig.GetElementsByTagName("permission") |
|
If (Nodes.Count > 0) Then |
|
|
For |
Each Node In Nodes |
|
|
RawXML.AppendFormat("<permission cref=""{0}"">{1}", _ |
|
|
Node.Attributes("cref").InnerText, _ |
|
|
vbCrLf) |
|
|
RawXML.AppendFormat("{0}{1}", Node.InnerXml, vbCrLf) |
|
|
RawXML.AppendFormat("</permission>{0}", vbCrLf) |
|
Next |
|
|
End If |
|
|
' Наконец, узлы "exception". |
|
|
Nodes = |
XmlDocOrig.GetElementsByTagName("exception") |
|
If (Nodes.Count > 0) Then |
|
|
For |
Each Node In Nodes |
|
|
RawXML.AppendFormat("<exception cref=""{0}"">{1}", _ |
|
|
Node.Attributes("cref").InnerText, _ |
|
|
vbCrLf) |
|
|
RawXML.AppendFormat("{0}{1}", Node.InnerXml, vbCrLf) |
|
|
|
|
см. след. стр.

386 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
RawXML.AppendFormat("</exception>{0}", vbCrLf)
Next
End If
Func.DocComment = FinishOffDocComment(RawXML.ToString())
End Sub
Разработав обновление документирующих комментариев, я подумал, что не! плохо было бы реализовать обработку контекста отмены. Благодаря этому вы могли бы в случае ошибки нажать Ctrl+Z и восстановить все изменения, сделанные Com! menTater. Увы, контекст отмены представляет реальную проблему. Когда у меня нет открытого контекста отмены, все изменения вносятся в документирующие комментарии прекрасно. Однако при открытии перед внесением изменений кон! текста отмены все путается и выглядит так, будто контекст отмены и элементы кода мешают друг другу. Когда CommenTater выполняет запись в свойство DocCom ment, стартовые точки элементов кода не обновляются, в результате чего обнов! ление происходит по старым позициям, повреждая файл. Я обнаружил, что, если вместо использования контекста отмены для глобального внесения всех измене! ний применять его для обновления каждого комментария к методу или свойству, все работает. Это хуже, чем глобальная отмена всех изменений, но хоть какая!то форма отмены. Надеюсь, Microsoft решит проблему с контекстом отмены, чтобы вы могли использовать его глобально для отмены крупномасштабных изменений.
Одна интересная проблема была связана с зарезервированными символами XML, которые вполне могут содержаться в именах функций. Ваша функция может на! зываться operator &, но вторая попытка использовать символ & в документирую! щем комментарии XML приведет к исключению, указывающему на некорректный символ. Конечно, это же справедливо для символов > и <, поэтому операторы operator <, operator >, operator << и operator >> также вызовут проблемы. Чтобы синтакси! ческий анализатор XML не жаловался, функция BuildFunctionComment в CommenTater производит все нужные замены (например, подставляет & вместо &).
CommenTater — очень полезный макрос, но вы могли бы внести в него одно прекрасное дополнение, работая над которым вы к тому же очень многое узнали бы об объектной модели IDE. Учитывая наличие тэга <exception>, вы могли бы документировать генерируемые функцией исключения. Попробуйте сделать так, чтобы ваш код искал функцию каждого оператора throw и автоматически добав! лял новый элемент для этого конкретного типа исключения. Конечно, когда ме! тод больше не генерирует исключение, вам следует отмечать соответствующие тэги <exception> как требующие удаления.
Стандартный вопрос отладки
Есть ли какие-нибудь хитрости отладки макросов и надстроек, написанных на управляемом коде?
Приступив к созданию макросов и надстроек, вы очень быстро заметите, что IDE Visual Studio .NET безумно любит поглощать исключения. Конечно, IDE хочет, чтобы никакие исключения не нарушали ее работу, но она с та! ким усердием пережевывает все необработанные исключения, что вы мо!

ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
387 |
|
|
жете даже не подозревать, что ваша программа генерирует исключения. Когда
яразрабатывал свои первые макросы, я минут 20 сидел, удивляясь, почему
яне могу достигнуть установленной точки прерывания.
Вконце концов, чтобы исключить сюрпризы, я открыл окно Exceptions, выбрал в дереве исключений узел Common Language Runtime Exceptions (исключения общеязыковой исполняющей среды) и отметил в блоке When The Exception Is Thrown (что делать при генерировании исключения) пункт Break Into The Debugger (выходить в отладчик) (рис. 9!4). Вероятно, после этого вы гораздо чаще будете прерываться в отладчике, но зато это изба! вит вас от любых сюрпризов.
На рис. 9!4 вы можете увидеть, что я не выбрал узел JScript Exceptions, потому что при разработке надстроек я не использую JScript .NET. Если вы достаточно храбры для создания надстроек на JScript .NET, выберите и этот узел, чтобы прерываться на всех исключениях.
Рис. 9 4. Параметры окна Exceptions, приказывающие выходить
вотладчик при всех исключениях
Авот при отладке макросов я заметил, что, даже если в окне Exceptions указано выходить в отладчик при всех исключениях, это может быть не так. Чтобы отладчик макросов начал работать правильно, после настройки окна Exceptions установите где!нибудь в своем коде точку прерывания.
Введение в надстройки
Макросы прекрасно подходят для решения небольших изолированных задач, но, если вам нужен более развитый пользовательский интерфейс, расширенные воз! можности ввода или вы хотите защитить свой исходный код, надо написать над! стройку. Разрабатывать макросы гораздо проще, однако надстройки позволяют решать некоторые задачи, перед которыми макросы бессильны, а именно:

388 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
добавление в IDE собственных инструментальных и диалоговых окон;
добавление в IDE собственных командных панелей (т. е. меню и панелей ин! струментов);
добавление собственных страниц свойств в диалоговое окно Options.
Как вы скоро увидите, разрабатывать и отлаживать надстройки намного труд! нее, чем макросы, поэтому я рекомендую пытаться сделать все, что можно, используя макросы, и только в случае неудачи приступать к сражению с надстройками.
По своей сути надстройки — это объекты COM, подключающиеся к IDE. Если вы волновались о том, как бы не забыть все, что вы изучили о COM за последние годы, не беспокойтесь: кое!что вам понадобится в мире надстроек. Интересно, что вы можете писать свои надстройки на Visual Basic .NET или C#, потому что управ! ляемые языки тоже поддерживают COM. Как и многим из вас, мне очень нравит! ся C++, но повышение производительности, обеспечиваемое .NET, мне нравится еще больше, поэтому в этой главе я буду основное внимание уделять вопросам, связанным с созданием надстроек на управляемых языках.
Как обычно, путешествие в мир надстроек следует начать с документации. Посетите также страницу http://msdn.microsoft.com/vstudio/downloads/samples/ automation.asp, которая содержит все примеры надстроек и мастеров от Microsoft. Вам непременно захочется провести за чтением кода этих примеров немало вре! мени, так как лучшего способа обучения еще никто не придумал.
Многие надстройки реализованы на нескольких языках, поэтому никаких про! блем в данном смысле возникнуть не должно. Некоторые из более сложных при! меров, таких как RegExplore, доступны только на C++. Замечу, что код C++ в при! мерах Microsoft — прекрасный пример плохого программирования. Значитель! ная часть кода изобилует магическими макросами, которые обрабатывают ошиб! ки при помощи goto и основаны на предполагаемых именах. Печально, но похо! жий код генерирует и мастер создания надстроек (Add!In wizard). Если вы реши! те писать надстройки на C++, не следуйте примеру Microsoft!
Не знаю, как насчет всего остального, но вам обязательно захочется исполь! зовать пакет Unsupported Tools (неподдерживаемые средства). Вы можете или загру! зить его с указанного сайта, или найти его текущую версию на CD, в каталоге UnsupportedAddInTools. Этот пакет включает программу Generate ICO Data for Extensibility (генерирование данных ICO для системы расширяемости) (Generate! IcoData.exe), которая генерирует шестнадцатеричные данные для значка, нужные для его вывода в окне About. Как это сделать, я покажу ниже. В Unsupported Tools входит также отличная надстройка Extensibility Browser (браузер расширяемос! ти) (ExtBrws.dll), которая отображает все свойства с поздним связыванием для объекта DTE (Development Tools Environment, среда инструментов разработки), корневого объекта в модели расширяемости Visual Studio .NET. Так как некоторые из этих свойств не очень хорошо описаны в документации, отображение их при помощи ExtBrws.dll может оказаться полезным. Если вы считаете себя опытным COM!программистом, можете просмотреть эти свойства, используя программу OLE/ COM Object Viewer.
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
389 |
|
|
Исправление кода, сгенерированного мастером Add-In Wizard
Если надстройку C# или Visual Basic .NET вы создаете, используя мастер Add!In Wizard среды Visual Studio .NET, который можно найти в диалоговом окне New Project (новый проект) в каталоге Other Projects\Extensibility Projects (другие про! екты\проекты расширяемости), сгенерированный им код может требовать неко! торых исправлений. В этом разделе я хочу рассказать о том, что сделать сразу после создания скелета надстройки, чтобы облегчить процесс разработки и не обезу! меть, решая проблемы в созданном коде. По ходу дела я укажу на ряд важных подробностей работы надстроек.
Нажав кнопку Finish в мастере надстроек, в самую первую очередь надо открыть редактор реестра. Мастер надстроек создает некоторые параметры реестра, ко! торые нужно экспортировать в REG!файл. Путь к нужному разделу реестра начи! нается или на HKEY_LOCAL_MACHINE, или на HKEY_CURRENT_USER в зависимости от того, указали ли вы, чтобы надстройка была доступна всем пользователям. Оставшаяся часть пути одинакова: \Software\Microsoft\VisualStudio\7.1\AddIns\<имя надстройки>. Сохраните все параметры, относящиеся к этому разделу, который далее я буду называть разделом надстройки.
Изучив содержание раздела, созданного мастером надстроек, вы заметите, что роль некоторых параметров, например, AboutBoxDetails, AboutBoxIcon, FriendlyName
и Description, в пояснениях не нуждается. Пара других параметров требует более подробного рассмотрения, так как они очень важны для отладки и разработки надстройки. Первый — CommandPreload — определяет, приказать ли надстройке за! регистрировать команды, которые она, возможно, хочет зарегистрировать. Мно! гие из моих проблем при отладке надстроек были связаны с некорректной реги! страцией команд.
Описание CommandPreload в документации, похоже, ошибочно: это не булево поле. Когда CommandPreload имеет значение 1, Visual Studio .NET загружает надстройку для регистрации ее команд; если 2 — полагает, что надстройка уже зарегистрировала свои команды. Если у вас возникли проблемы с выполнением команд надстрой! ки, присвойте CommandPreload значение 1 и перезагрузите IDE, чтобы гарантиро! вать их регистрацию.
Параметр LoadBehavior характеризует загрузку надстройки. Если данное бито! вое поле равно 0, значит, ваша надстройка не загружается. Значение 1 указывает, что надстройка должна быть загружена при запуске IDE; 4 — что надстройка дол! жна быть загружена при компоновке программы из командной строки. В Visual Studio .NET 2002 была одна проблема: при компоновке из командной строки над! стройки загружались всегда, даже если вы указывали не использовать их в таких случаях. К счастью, в Visual Studio .NET 2003 эта ошибка исправлена.
Есть два параметра реестра, которые не создаются мастером надстроек по умолчанию, но их нужно добавить, если вы хотите использовать собственные растровые изображения на панели команд или другие ресурсы Win32. Это пара! метры SatelliteDllName и SatelliteDllPath. Работать с собственными управляемы! ми растровыми изображениями и ресурсами в управляемых надстройках было бы весьма удобно, но Visual Studio .NET требует только COM, поэтому вы должны поместить свои ресурсы в DLL ресурсов Microsoft Win32. Как можно догадаться

390 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
по названиям, SatelliteDllName содержит только имя DLL, а SatelliteDllPath содержит путь к сателлитной DLL. В документации к SatelliteDllPath говорится, что IDE в конечном счете будет искать DLL по указанному пути (во время предыдущих по! пыток поиска к указанному пути прибавляются региональные идентификаторы), но не загрузит ее оттуда, и вы не получите никаких ресурсов. Например, если SatelliteDllPath содержит путь C:\FOO\ и вы работаете на компьютере, настро! енном на американский вариант английского языка, ваша сателлитная DLL долж! на находиться в каталоге C:\FOO\1033.
Задав сателлитную DLL, вы можете локализовать значения, указываемые в раз! деле реестра вашей надстройки. Если указанное вами строковое значение состо! ит из символа #, за которым следует число, IDE будет искать это значение в стро! ковой таблице вашей сателлитной DLL. Сателлитные DLL используются обеими надстройками из этой главы: и SuperSaver, и SettingsMaster.
Нам осталось рассмотреть один странный параметр реестра — AboutBoxIcon. Он содержит код значка надстройки, который вы хотите вывести в окне About. Как я говорил выше, этот шестнадцатеричный код может быть сгенерирован програм! мой GenerateIcoData из состава Unsupported Tools. Его нужно скопировать в поле параметра AboutBoxIcon, имеющее тип REG_BINARY.
Оба проекта — и SuperSaver, и SettingsMaster — включают файлы <имя проек! та>.ADDIN.REG, присваивающие нужные значения всем параметрам обеих надстро! ек. Эти REG!файлы позволяют удалять и быстро восстанавливать нужные значе! ния реестра, облегчая установку. Единственный их недостаток в том, что вы дол! жны жестко закодировать значение SatelliteDllPath.
Наведя порядок со значениями реестра, нужно заняться исправлением сгене! рированного мастером кода. Вероятно, сначала вам захочется изменить атрибут ProgId, ассоциированный с созданным классом Connect. Мастеру нравится добав! лять к имени надстройки слово «.Connect», что излишне. К сожалению, мастер надстроек во многих местах жестко кодирует имя команды, поэтому, если вы уда! лите из атрибута ProgId слово «.Connect», измените еще несколько мест:
раздел надстройки в реестре;
использование команды в методе QueryStatus (файл CONNECT.CS/.VB);
использование команды в методе Exec (файл CONNECT.CS/.VB).
Янастоятельно рекомендую создавать для имен команд константы и исполь! зовать их везде, где требуются имена. Для своих надстроек я создал файл RESCON! STANTS.CS/.VB, содержащий все константы для всех команд. Так я исключаю про! блемы с опечатками, и, если мне хочется изменить имя команды, сделать это очень просто.
Наверное, самый большой недостаток кода, сгенерированного мастером, в том, что он глотает исключения при регистрации команд и добавлении элементов на панели инструментов. Когда я только начал разрабатывать надстройки, я недоуме! вал, почему некоторые из моих команд недоступны. Оказалось, они не регистри! ровались, потому что регистрация генерировала исключение, вызывавшее пропуск оставшейся части функции. Сгенерированный мастером код похож на следующий фрагмент, и это довольно опасно. Выполняя обзоры кода, вы обязательно долж! ны убеждаться, что пустые выражения catch представляют собой что!то действи! тельно безопасное.
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
391 |
|
|
|
|
try |
|
|
{ |
|
|
Command command = commands.AddNamedCommand (...) ; |
|
|
CommandBar commandBar = (CommandBar)commandBars["Tools"] ; |
|
|
CommandBarControl commandBarControl = |
|
|
command.AddControl ( commandBar , |
|
|
1 |
) ; |
|
} |
|
|
catch(System.Exception /*e*/)
{
}
Все действия по созданию команд и панелей инструментов выполняются в надстройках по умолчанию в методе OnConnection, когда параметр режима под! ключения содержит значение ext_cm_UISetup. Я всегда выношу создание команд и панелей инструментов в отдельный метод, за пределы OnConnection. Между прочим, при режиме подключения ext_cm_UISetup ваша надстройка выгружается сразу после возврата из метода OnConnection. При режиме подключения ext_cm_Startup или ext_cm_AfterStartup надстройка перезагружается.
Перед регистрацией своих команд и добавлением командных панелей удаляйте все команды и панели инструментов, которые вы, возможно, уже добавили. Так вы гарантируете, что любые регистрируемые вами для надстройки команды и па! нели команд создаются «свежими». Удаление добавленных команд и панелей ин! струментов позволит также безопасно изменять параметры команд или команд! ных панелей и избегать проблем с исключениями, возможных при наличии пре! дыдущих элементов с тем же именем.
Для облегчения разработки надстроек я всегда создаю макрос, удаляющий команды и командные панели, создаваемые моими надстройками. Такой макрос я могу использовать и для уничтожения следов надстройки. Перед запуском мак! роса, удаляющего команды, ваша надстройка должна быть выгружена. Это зна! чит, что вы должны отключить надстройку в диалоговом окне Add!In Manager (диспетчер надстроек), закрыть запущенные копии IDE и удалить раздел надстрой! ки в реестре.
Если методам создания команд и панелей инструментов что!то не нравится, они генерируют исключения. Обязательно помещайте все, что можно, в блоки try...catch и сообщайте о причинах исключений, чтобы знать, что происходит. Примеры удаления и установки команд и командных панелей вы увидите в коде надстроек SuperSaver и SettingsMaster.
Решение проблем с кнопками панелей инструментов
Исправив код, сгенерированный мастером надстроек, вы, вероятно, столкнетесь с проблемой правильного показа растровых изображений на панелях инструмен! тов. Решить ее нетрудно, но это не описано в документации. Поиск заклинаний потребовал от меня некоторых усилий, поэтому я надеюсь, что это обсуждение поможет вам сэкономить время и сберечь нервы.
Для загрузки на панель инструментов собственные растровые изображения нужно разместить в сателлитной DLL Win32; встроенные управляемые растровые изображения на панелях инструментов использовать нельзя. Создавая команду