Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
392 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
методом Commands.AddNamedCommand, вы должны передать ему значение false в пара! метре MSOButton и идентификатор ресурса растрового изображения в сателлитной DLL в параметре Bitmap.
Самые неприятные проблемы с размещением собственных растровых изоб! ражений на панелях инструментов — сами изображения! Во!первых, поддержи! ваются только 16!цветные изображения. Если вы находите свое изображение стран! ным, значит, в нем используется больше цветов. Вторая проблема состоит в полу! чении правильной маски.
При взгляде на растровые изображения в надстройке RegExplorer мне показа! лось, что в них в качестве цвета маски применяется зеленый. Получить правиль! ную маску очень важно, так как именно благодаря этому ваше изображение мо! жет казаться трехмерным при наведении на него курсора. При создании изобра! жений для своих кнопок я также использовал в качестве маски зеленый цвет. Но, когда я загрузил свою надстройку, маска определенно не работала, и все места, которые я хотел сделать прозрачными, имели безобразный ярко!зеленый цвет. В ходе расследования я выяснил, что на самом деле в качестве маски нужно было использовать не истинный зеленый, а цвет с RGB!значением 0, 254, 0 (зеленому соответствует 0, 255, 0).
Однако даже после изменения в палитре значения зеленого цвета на 0, 254, 0 маска осталась зеленой. Оказалось, что я использовал устаревший графический редактор, который так хотел мне «помочь», что «исправлял» палитру, отображая зеленый как 0, 255, 0, а не так, как я хотел. Тогда я с помощью редактора растро! вых изображений Visual Studio .NET переназначил один из цветов палитры (я всегда устанавливаю вместо розового, принятого по умолчанию, цвет 0, 254, 0), и все по! лучилось. Помните: когда вы откроете растровое изображение в редакторе Visual Studio .NET, он изменит зеленый цвет в палитре на 0, 254, 0, потому что это бли! жайший цвет к зеленому. Это значит, что, если вы хотите использовать в своем изображении истинный зеленый цвет, вам придется изменить значение другого элемента на 0, 255, 0.
Исправив цвет маски, следует обновить и свой код, чтобы гарантировать, что ваши панели инструментов не будут отличаться от других. Кнопки панелей инст! рументов, добавляемые к объекту CommandBar, по умолчанию отображаются как кнопки с текстом. Чтобы они отображались как стандартные, нужно вручную перебрать все элементы CommandBarControl объекта CommandBar и присвоить им стиль MsoButtonStyle.msoButtonIcon. Вот как я сделал это в SuperSaver:
foreach ( CommandBarControl ctl in SuperSaverCmdBar.Controls )
{
if ( ctl is CommandBarButton )
{
CommandBarButton btn = (CommandBarButton)ctl ; btn.Style = MsoButtonStyle.msoButtonIcon ;
}
}
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
393 |
|
|
Создание окон инструментов
Почти все надстройки, добавляющие команды, имеют и панель инструментов с растровыми изображениями, но иногда желательно добавить в Visual Studio .NET полноценный пользовательский интерфейс. Отобразить из надстройки управля! емое диалоговое окно не сложнее, чем из приложения Windows Forms. Для ото! бражения полного окна, называемого окном инструментов, требуется чуть боль! ше работы.
В IDE Visual Studio .NET два типа окон: документов и инструментов: в окнах до! кументов вы редактируете код. Все остальные — это окна инструментов (напри! мер, приведу окна Task List, Solution Explorer и Toolbox). Окно инструментов мо! жет быть стыкуемым или, если вы щелкнете правой кнопкой его заголовок и от! мените параметр Dockable, оно может отображаться как полное окно в основной области редактирования.
Так как все окна инструментов являются COM!объектами, их можно создавать на C++ и бороться со всеми неприятностями, которые за этим последуют. Созда! ние окон инструментов на управляемом коде в документации не описано, но зато в число предоставляемых Microsoft примеров входит подобный проект, грамот! но названный ToolWindow.
Суть создания управляемого окна инструментов в том, чтобы ваша управляе! мая надстройка создала компонент ActiveX, который в свою очередь обеспечива! ет работу CLR. Как только это сделано, можно приказать компоненту ActiveX за! грузить и отобразить в окне ActiveX нужный элемент управления. Этот компонент ActiveX иногда называют элементом управления «хост!прослойка» (host shim control), так как он просто внедряется в выполнение управляемого кода, чтобы вы могли сделать то, что вам нужно.
Все звучит так, как будто этот «хост!прослойку» написать очень сложно, но есть хорошая новость: он содержится в примере ToolWindow, и вы можете его исполь! зовать. Увы, он почти не проверяет ошибок, поэтому, если что!то потерпит крах, вам останется только чесать голову. А теперь самое приятное: я проработал весь код этого элемента, реализовал проверку ряда ошибок и добавил диагностические выражения, чтобы вы знали, что происходит при его использовании.
Я переименовал свой «хост!прослойку» в VSNetToolHostShim и включил в пример SimpleToolWindow на CD. Все, что делает SimpleToolWindow, заключается в добав! лении в IDE Visual Studio .NET окна редактирования a la окно WinDBG. Так как создание элемента управления «хост!прослойка» контролируется вашей управля! емой надстройкой, вы можете использовать VSNetToolHostShim из любого своего проекта окна инструментов.
Объяснить необходимые действия проще всего на примере обработчика OnCon nection из проекта SimpleToolWindow. Вы должны создать окно инструментов с элементом управления VSNetToolWinShim, который возвращает ссылку на элемент управления VSNetToolHostShim. Используя возвращенный объект VSNetToolHostShim, вызовите метод HostUserControl2, чтобы загрузить свой управляемый элемент и создать кнопку для открытия окна инструментов. Все это в действии можно уви! деть в листинге 9!3.
394 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Листинг 9-3. Использование VSNetToolHostShim
public void OnConnection ( object |
application , |
|
ext_ConnectMode |
connectMode |
, |
object |
addInInst |
, |
ref System.Array |
custom |
) |
{ |
|
|
try
{
ApplicationObject = (_DTE)application;
AddInInstance = (AddIn)addInInst;
//Ваше окно инструментов должно иметь уникальный GUID. String guid = "{E16579A4 5E96 4d84 8905 566988322B37}" ;
//Объект для получения элемента VSNetToolHostShim. Object RefObj = null ;
//Создание основного окна инструментов
//путем загрузки "хоста прослойки".
TheToolWindow = ApplicationObject.Windows. |
|
CreateToolWindow ( AddInInstance |
, |
"VSNetToolHostShim.VSNetToolWinShim", |
|
"Scratch Pad Window" |
, |
guid |
, |
ref RefObj |
); |
//До вызова метода HostUserControl нужно сделать
//окно видимым, иначе все пойдет не по плану. TheToolWindow.Visible = true ;
//Получение "прослойки"(это переменная уровня класса):
//private VSNetToolHostShimLib.IVSNetToolWinShim ShimObj ; ShimObj = (VSNetToolHostShimLib.VSNetToolWinShimClass)
RefObj ;
//Получение данной сборки. Это нужно, чтобы я мог
//передать "прослойке" расположение надстройки. System.Reflection.Assembly CurrAsm =
System.Reflection.Assembly.GetExecutingAssembly ( ) ;
//Получение каталога данной надстройки и присоединение к пути
//имени DLL ресурсов. Это нужно для загрузки кнопки ярлычка. StringBuilder StrSatDll = new StringBuilder ( ) ;
String StrTemp = CurrAsm.Location.ToLower ( ) ;
int iPos = StrTemp.IndexOf ("simpletoolwindow.dll" ) ; StrSatDll.Append ( CurrAsm.Location.Substring ( 0 , iPos )); StrSatDll.Append ("SimpleToolWindowResources.DLL" ) ;
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
395 |
|
|
//Этот метод загружает управляемый элемент в элемент управления
//ActiveX и приказывает ему загрузить растровое изображение.
ShimObj.HostUserControl2 ( TheToolWindow |
, |
CurrAsm.Location |
, |
"SimpleToolWindow.ScratchPadControl" , |
|
StrSatDll.ToString ( ) |
, |
1 |
); |
} |
|
catch ( System.Exception eEx ) |
|
{ |
|
MessageBox.Show ( eEx.Message + "\r\n" + |
|
eEx.StackTrace.ToString ( ) |
, |
"ExceptBion in OnConnection" |
) ; |
} |
|
}
Создание на управляемом коде страниц свойств окна Options
Создать управляемое окно инструментов относительно легко. Разработать управ! ляемую страницу свойств, отображаемую в диалоговом окне Options (рис. 9!5), немного сложнее. Это важно потому, что именно в окне Options пользователи обычно будут искать страницу изменения параметров вашей надстройки, и так вы сможете улучшить свою репутацию.
Рис. 9 5. Страница свойств надстройки SettingsMaster в окне Options
Как вы, наверное, уже догадались, страница свойств в окне Options представ! ляет собой элемент управления ActiveX, реализующий интерфейс IDTToolsOptionsPage. Чтобы узнать, есть ли у вас такая страница свойств, Visual Studio .NET изучает раздел надстройки в реестре. В основном разделе надстройки она ищет раздел Options. В разделе Options будут находиться один или больше разделов, которые будут до! бавлены в дерево окна Options как узлы верхнего уровня. У вас будет один раздел
396 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
этого уровня, названный так же, как и надстройка. В этом разделе будет находиться очередной набор разделов, формирующих подузлы верхнего узла дерева. По умол! чанию первый раздел называется General. В каждом заключительном разделе бу! дет находиться строковый параметр Control, содержащий ProgID элемента управ! ления ActiveX, создаваемого для отображения страницы свойств.
Наверное, лучше всего проиллюстрировать сказанное на примере. Разделы реестра для страницы свойств SettingsMaster (рис. 9!5) выглядят так:
HKEY_CURRENT_USER\ |
|
Software\ |
|
Microsoft\ |
|
VisualStudio\ |
|
7.1\ |
|
AddIns\ |
|
SettingsMaster\ |
<— Раздел надстройки |
Options\ |
<— Раздел Options |
SettingsMaster\ |
<— Корневой узел в окне Options |
General |
<— Подузел узла SettingsMaster |
Параметр в разделе General:
Control REG_SZ SettingsMasterShim.SettingsMasterOption
Создание страниц свойств и управление ими контролируется диалоговым ок! ном Options, а не вашей надстройкой; когда дело касается разработки собствен! ных страниц свойств на управляемом коде, это представляет небольшую пробле! му. Дело в том, что в каждом конкретном случае запускается элемент управления ActiveX, указанный в строке Control. Это значит, что созданный элемент управле! ния ActiveX должен иметь априорные знания об управляемом элементе, который вы хотите отобразить. Я прямо ломал голову над этим, когда на мой стол попал февральский номер «MSDN Magazine» за 2002 год, в котором Лео Нотенбум (Leo Notenboom) описал великолепное решение проблемы.
Лео предлагает, чтобы всю работу выполнял «элемент!прослойка» ActiveX, на! писанный на C++. Так как в отличие от окон инструментов создать общий эле! мент управления ActiveX для страниц свойств в окне Options нельзя, вы должны будете создавать новый элемент управления для каждого проекта. К счастью, для этого нужно только взять код Лео, изменить GUID и имя элемента управления в файлах .RGS и изменить GUID загружаемого элемента управления в коде C++. Если хотите полностью разобраться в решении Лео, прочитайте его прекрасную статью.
Чтобы облегчить поиск проблем, я добавил в код Лео несколько диагностичес! ких выражений и обработчиков ошибок. Если вы решите использовать проекты SuperSaverOptionsShim или SettingsMasterShim с CD, найдите в главных CPP!фай! лах строки k_HOSTCLSID и замените в них значение GUID на GUID вашей конкрет! ной страницы свойств. Естественно, нужно изменить и имя элемента управления и его GUID в файлах .RGS.
Когда я отобразил свои страницы свойств, мне показалось, что все отлично. Но, загрузив надстройку на ноутбуке и взглянув на страницу свойств в окне Options, я понял, что что!то не так: моя страница не была похожа на остальные страницы свойств. Чтобы диалоговые окна и окна инструментов выглядели лучше на экра! не моего ноутбука (который имеет совершенно безумное разрешение), я прибег!
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
397 |
|
|
нул к одной небольшой хитрости и, открыв папку Environment (среда) и выбрав узел Fonts And Colors (шрифты и цвета), изменил шрифт диалоговых окон и окон инструментов (Dialogs And Tool Windows). Элементы управления, разработанные на управляемом коде, используют по умолчанию фиксированный шрифт Microsoft Sans Serif размером 8,25 точки, поэтому я не мог узнать корректный шрифт у хо! стов этих элементов, а должен был искать определенный для хоста шрифт сам.
Открыв проект SuperSaver, вы увидите элемент управления OptionPropPage! Base.CS. Это базовый класс, который определяет текущий тип и размер шрифта диалогового окна и применяет эти параметры для всех элементов управления на странице. Вам может показаться, что получение типа и размера шрифта — триви! альная задача, но подобные свойства позднего связывания большей частью не документированы. Вот почему упомянутый выше Extensibility Browser из пакета Unsupported Tools иногда может оказаться крайне полезным. Как только я узнал нужные заклинания, все оказалось просто. Вот код метода OptionPropPageBase.On AfterCreated, который получает нужный шрифт, создает его и настраивает диало! говое окно и все элементы управления (листинг 9!4):
Листинг 9-4. Получение и установка шрифтов для страницы свойств в окне Options
public virtual void OnAfterCreated ( DTE DTEObject )
{
//Чтобы гарантировать правильное отображение этой страницы свойств,
//я должен выбрать в качестве всех шрифтов тот шрифт, который выбран
//пользователем в пункте Dialog and Tool Windows. Чтобы получить
//значения из свойств DTE, я использую позднее связывание. Properties Props = DTEObject.get_Properties ( "FontsAndColors",
"Dialogs and Tool Windows" );
String FntName = (String)Props.Item ( "FontFamily" ).Value ;
Object ObjTemp = Props.Item ( "FontSize" ).Value ;
Int32 FntSize = Convert.ToInt32 ( ObjTemp ) ;
// Создание шрифта. |
|
Font DlgFont = new Font ( FntName |
, |
FntSize |
, |
GraphicsUnit.Point |
) ; |
//Установка шрифта для диалогового окна. this.Font = DlgFont ;
//Перебор всех элементов управления в диалоговом окне и установка
//их шрифтов. Некоторые элементы будут использовать шрифт, заданный
//чуть выше, но не все, поэтому мне нужно сделать это вручную. foreach ( Control Ctl in this.Controls )
{
Ctl.Font = DlgFont ;
}
}
398 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
Конечно, задать шрифт для завершения работы недостаточно. Хотя элементы управления типа «метка» (label control) могут настраивать свои размеры автома! тически, большинство элементов управления на это неспособно, поэтому вам нужно будет просмотреть их и увеличить размеры. Чтобы узнать, как я реализовал на! стройку размеров для конкретного диалогового окна, изучите мой метод SuperSaver Options.OnAfterCreated.
Возможно, вас интересует, почему я не реализовал изменение шрифтов диа! логовых окон «на лету» после того, как кто!нибудь это запросит. Хочу вас обра! довать: шрифты диалоговых окон можно изменить путем простого перезапуска IDE. Интересно, что Visual Studio .NET позволяет изменять «на лету» все шрифты, кроме шрифтов диалоговых окон.
Мой крутой класс из файла OptionPropPageBase.CS позаботится за вас кое о чем, но из!за ошибки в Visual Studio .NET его очень сложно использовать. Если вы уна! следуете свой элемент управления от OptionPropPageBase, IDE не будет открывать его в режиме разработки (design mode), а будет рассматривать его как обычный текстовый файл. Чтобы Visual Studio .NET загружала элемент управления в режи! ме разработки, позволяя редактировать его при помощи дизайнера, в качестве ба! зового для него нужно временно указывать класс System.Windows.Forms.UserControl. Надеюсь, Microsoft исправит эту проблему в пакете обновлений или в будущей версии Visual Studio.
Стандартный вопрос отладки
Моя сборка загружается только из моей надстройки. Должен ли я устанавливать ее в глобальный кэш сборок (global assembly cache, GAC)?
GAC — специальное место, и вам не следует устанавливать в него что!либо без крайней нужды. К счастью, разработчики IDE Visual Studio .NET преду! смотрели в каталоге <каталог установки VS.NET>\Common7\IDE два подка! талога для сборок только надстроек или макросов: PublicAssemblies и Private! Assemblies. Если вы хотите, чтобы код в вашей сборке могли вызывать дру! гие надстройки или макросы, поместите сборку в каталоге PublicAssemblies. А чтобы сборка вызывалась только вашей надстройкой, сохраните ее в ка! талоге PrivateAssemblies.
Стандартный вопрос отладки
Есть ли более простые способы отладки надстроек,
если уж они могут загружаться в IDE, используемую для отладки?
Отладка надстройки может быть очень сложной, так как для правильного конфигурирования экземпляров IDE, которые вы хотите использовать для отладки, нужно удалить раздел надстройки в реестре, открыть проект над! стройки в целевой IDE и восстановить раздел надстройки. Это не очень трудно, но тут легко запутаться. Кроме того, если нужно скомпилировать надстройку после исправления ошибки и надстройка загружается IDE, ваша компоновка никогда не будет работать.
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
399 |
|
|
На помощь приходит недокументированный ключ командной строки /rootsuffix. Он приказывает Visual Studio .NET добавить суффикс к нормаль! ному имени раздела реестра и загрузить все пакеты, надстройки и параметры из этого раздела реестра, а не из раздела по умолчанию. Это похоже на наличие двух отдельных Visual Studio .NET на одном компьютере.
Для этого вы должны прежде всего запустить REGEDIT.EXE и найти раз! дел HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1. Выбрав этот раздел, выберите Export в меню File и сохраните разделы реестра в файл. Сохранив файл, откройте его в NOTEPAD.EXE и замените все выражения HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1 на HKEY_LO! CAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1NoAddIns. Заметьте, что к названию раздела надо добавить суффикс NoAddIns. Заменив все выра! жения, сохраните файл. Снова запустите REGEDIT.EXE, выберите Import в меню File и импортируйте измененный файл в реестр. Если хотите сделать то же самое со своими пользовательскими параметрами (кроме, конечно, любых надстроек!), выполните указанные действия с разделом HKEY_CUR! RENT_USER\Software\Microsoft\VisualStudio\7.1.
Чтобы Visual Studio .NET использовала раздел с суффиксом NoAddIns, просто запустите ее:
devenv /rootsuffix NoAddIns
Так вы получите загруженную копию Visual Studio .NET, в которой смо! жете делать с кодом своей надстройки что угодно без проблем.
Надстройка SuperSaver
Вы получили некоторое представление о надстройках, и я думаю, что сейчас луч! ше всего обсудить реальные надстройки. Первую созданную мной надстройку — SuperSaver — я представил в колонке Bugslayer в «MSDN Magazine». Однако ее вер! сия в этой книге радикально отличается от того варианта — прекрасный пример всех проб и ошибок, связанных с созданием надстроек!
SuperSaver решает две проблемы: добавляет в IDE Visual Studio .NET функцию автоматического фонового сохранения, чтобы вы не лишились измененного до! кумента, а при сохранении файлов удаляет пустые места в концах строк. В случае файлов C++ и C# IDE Visual Studio .NET, противореча компьютерным богам, ос! тавляет в концах строк пустое пространство. Чтобы вернуть в мир порядок, я просто обязан был исправить это недоразумение.
Реализация фонового сохранения файлов оказалась чуть ли не слишком лег! кой. Я устанавливаю фоновый таймер и при его срабатывании выполняю коман! ду, ассоциированную с пунктом Save All (сохранить все) меню File, или File.SaveAll. При удалении пустых мест возникли проблемы.
Работая над сохранением активного элемента, я заметил, что на самом деле выполняется команда File.SaveSelectedItems. Интересно, что вместо сохранения только редактируемого в данный момент документа она сохраняет также проект и все элементы, выбранные в Solution Explorer (для выбора нескольких элемен! тов нужно щелкнуть их, удерживания клавишу Ctrl). Мне хотелось, чтобы моя
400 ЧАСТЬ III Мощные средства и методы отладки приложений .NET
команда была полной заменой File.SaveSelectedItems, поэтому я должен был сы! митировать это поведение. Сначала я последовал такому алгоритму:
получить набор DTE.SelectedItems
для каждого элемента из числа выбранных элементов если это документ и он не сохранен,
получить текстовый документ
вызвать для текстового документа метод ReplacePattern конец блока если
вызвать для данного элемента метод Save следующая итерация цикла
Хотя алгоритм казался совсем простым, при его реализации я столкнулся с массой проблем. Пытаясь их решить, я разработал в итоге семь абсолютно раз! ных версий SuperSaver. Первая проблема заключалась в самих проектах. Не все типы проектов поддерживают метод Save. Наглядный пример — проекты Setup. Это значит, что у меня не было гарантированной возможности сохранения всех про! ектов. Я попытался разработать специальные случаи вызова метода Save, но это не позволило бы правильно обрабатывать все будущие типы проектов, которые также могут не поддерживать Save.
Вторая проблема была связана с ошибкой в объекте TextDocument. Я вызывал метод ReplacePattern с регулярным выражением «[ \t]+$» (без кавычек) в качестве шабло! на поиска и пустой строкой в качестве текста замены. Все бы прекрасно, но это выражение также очищало все пустые строки, оставленные просто ради пропус! ка. Поэтому, если я сохранял активный файл, содержащий 20!символьный отступ, сделанный при помощи символов табуляции или пробелов, автоматические от! ступы удалялись, и мне пришлось бы восстанавливать их. Клавиша табуляции мне нравится не меньше, чем вам, но я решил, что гораздо лучше исправить это, что! бы SuperSaver удалял пустые места только в тех строках, которые на самом деле содержат текст.
После многих часов борьбы с регулярными выражениями я обнаружил нуж! ное выражение поиска — «{[^ \t]+}{[ \t]#$}», а также выражение замены — «\1». За! мена с применением подвыражений — одно из самых больших достоинств регу! лярных выражений. Если вы не считаете себя гуру в регулярных выражениях, то это выражение поиска приказывает искать любые выражения, соответствующие первой группе (ограниченной первой парой фигурных скобок), т. е. содержащие любые символы, кроме пробела или табуляции. Это гарантирует, что соответствие будет найдено только в строках, содержащих символы. Вторая группа (во вторых фигурных скобках) ищет один или более пробелов или символов табуляции в конце строки. Соответствие этим двум группам будет найдено только в строках, содер! жащих после себя пустое пространство. Строка замены «\1» говорит анализатору регулярных выражений заменить найденное выражение текстом, соответствую! щим первой группе. В данном случае это символы в конце строки без пустого пространства. Так что пустое место из строки удаляется.
Я изменил параметр метода TextDocument.ReplacePattern на мое новое выраже! ние поиска и испытал сохранение в действии. Результатом стало то, что любые строки, завершавшиеся лишними пробелами, теперь заканчивались на заверша! ющий текст плюс символы «\1», заменяющие лишние пробелы! Это было очень
ГЛАВА 9 Расширение возможностей интегрированной среды разработки VS .NET |
401 |
|
|
интересно, но искажение кода, вероятно, не сделало бы SuperSaver очень полез ным. Оказалось, что в методе TextDocument.ReplacePattern допущена ошибка, нару шающая подстановку подвыражений.
Если я не мог заставить работать подстановку подвыражений регулярных вы ражений, надстройка SuperSaver была бы менее полезной. Рассматривая обходные пути решения этой проблемы, я записал макрос поиска и замены, который исполь зует глобальный объект Find и работает с подстановкой подвыражений. Этот способ не так чист, как применение метода ReplacePattern, но он хоть стал началом ре шения.
Объект Find помог мне быстро понять, что, так как это глобальный объект, любые его изменения во время подстановок подвыражений регулярных выражений в коде SuperSaver приводят к изменению текущих значений, оставленных пользователем в диалоговом окне Find. Поэтому я создал для Find оболочку SafeFindObject, позво ляющую сохранять и восстанавливать все значения, исключая потерю парамет ров пользователей.
Дополнительная проблема с глобальным объектом Find состояла в том, что, когда активным документом был дизайнер Windows Forms, я мог получить для документа объект TextDocument. Однако метод Find.Execute, выполняющий фактический поиск и замену, генерировал исключение. Разрабатывая SafeFindObject, я решил, что ничего не могу с этим поделать, кроме как «глотать» все исключения, генерируемые при вызове Find.Execute.
Все шло своим чередом, когда я по настоящему захотел, чтобы пустые места удалялись и при автосохранении. Проделав основную работу по очистке и сохра нению активного документа, я думал, что все будет OK. Увы, если я приказывал Find продемонстрировать свои чудесные способности на всех файлах и при этом были открыты какие нибудь файлы, допускавшие только чтение, надстройка не работала. Поэтому я просто реализовал перебор всех открытых файлов, и, если некоторые были отмечены как только для чтения, я не вызывал SafeFindObject для их сохранения с удалением пустых мест.
Кроме трудностей с автосохранением, я заметил проблему с «новыми» файла ми, т. е. несохраненными файлами, созданными командой New меню File. Вызов метода Save на этих файлах приводил к появлению диалогового окна Save File As (сохранить как). Я думал, что смогу найти внутри метода Save блокирующий вы зов, но его не оказалось, поэтому, поработав с IDE, я в итоге получал кучу окон Save File As, настроения не улучшавших.
Тут я стал немного одержим идеей доведения автосохранения файлов с удале нием пустых мест до логического завершения. Чтобы следить за ходом обсужде ния, откройте файл TrimAndSave.CS и перейдите к методу TrimAndSave.SaveAll. По смотрите закомментированный блок — «Original Attempt» (первая попытка). Сей час я расскажу, почему этот код не работает.
Так как я работал с текущими файлами при помощи объекта Find, я подумал, что могу перевести все требующие сохранения файлы в активное состояние и быстро их сохранить. Это казалось разумным, пока я не столкнулся с огромной проблемой. Если я работал с документом Windows Forms, открытым в окнах про ектирования (design view) и кода (code view), вызов метода Active всегда активи зировал окно проектирования, даже когда активным было окно кода. Это означа
