© Эрик Берр (Erik Berry),
консультант и разработчик ПО,
Oasis Digital Solutions
Переведено БНТП по заказу Interface Ltd.
Аннотация: с помощью набора интерфейсов Open Tools API (OTA) для C#Builder разработчики могут расширить функциональные возможности сред разработки (IDE), используя любой язык .NET. В этой статье рассматриваются важные особенности новой .NET OTA и предлагаются несколько практических примеров и советов.
Набор интерфейсов Open Tools API (OTA) предоставляет разработчикам удобное средство расширения и улучшения функциональных возможностей среды разработки C#Builder. Поскольку OTA для C#Builder практически ничем не отличается от аналогичных интерфейсов, поставляемых к продуктам Delphi и C++Builder , разработчики, близко знакомые с этими инструментами, не должны испытывать никаких проблем при создании расширений для C#Builder. Данная статья знакомит опытных разработчиков с важными изменениями и дополнениями в новом .NET OTA, а также дает общее представление о процессе разработки и интерфейсах для разработчиков, специализирующихся в новом OTA. Используемый в этой статье пример кода можно загрузить из CodeCentral.
OTA содержится в наборе имен Borland.Studio.ToolsAPI в сборке (assembly) Borland.Studio.ToolsAPI.dll. C#Builder имеет базовую документацию о содержимом этой сборки, но для ее прямого просмотра можно использовать Borland Reflection.exe или браузер .NET Reflector. При создании дополнительных сборок OTA необходимо добавлять ссылку в сборку Borland.Studio.ToolsAPI.dll, которая расположена в каталоге BDS\1.0\Bin.
Поскольку каждому вводному разделу требуется базовый пример "Hello World", используем его для C#Builder. Предлагаемые в этой статье примеры написаны на языке C#, но для создания расширений OTA годится любой язык .NET. Описываемый ниже класс реализует интерфейс IOTAMenuWizard, который является основным типом расширений IDE. Этот интерфейс добавляет новый пункт в меню "Help" и может выполнить соответствующий код при выборе этого пункта. Создайте новую сборку .NET, последовательно выбрав File, New, Other, C# Projects, Class Library. Добавьте новую ссылку в сборку Borland.Studio.ToolsAPI.dll, выбрав Projects, Add Reference и щелкнув по кнопке Browse. Также добавьте соответствующую ссылку в сборку System.Windows.Forms.dll, чтобы открыть доступ к классу MessageBox. Затем отредактируйте код класса, чтобы он имел следующий вид:
using System; using Borland.Studio.ToolsAPI; using System.Windows.Forms; namespace ToolsAPI.Introduction |
Этот пример иллюстрирует два основных типа интерфейсов, которые можно найти в OTA. Кроме них, IDE реализует большое количество других интерфейсов (включая все интерфейсы, в именах которых фигурируют "Service" или "Manager"). Одним из таких интерфейсов является IOTAWizardService, который позволяет добавлять и удалять экспертов (термин OTA для "родных" расширений) в среде IDE. Другой важный тип интерфейсов – это тот, который разработчик должен реализовать, чтобы обеспечить взаимодействие IDE с его кодом. Например, для добавления нового пункта меню с помощью интерфейса IOTAMenuWizard следует указать IDE текст этого пункта, будет ли этот пункт доступен, уникальный идентификатор используемого эксперта и т.п.
После компиляции сборку необходимо загрузить в IDE, для этого следует добавить строку нового входа в папку системного реестра:
HKCU\Software\Borland\BDS\1.0\Known IDE Assemblies
Имя входа (Name) должно содержать полный путь и имя файла скомпилированной сборки dll. Строка данных входа (Data) может быть произвольной, но для загрузки сборки в IDE она должна содержать значение:
C:\OTAIntro\OTAIntro.dll = OTAIntro
После того, как указанный вход будет добавлен в реестр, IDE автоматически загрузит сборку при запуске. Кроме того, IDE попытается определить местонахождение функции "public static void" с именем IDERegister. Если IDE найдет такой метод, то он будет выполнен и укажет место, где следует инициализировать и регистрировать расширение. После загрузки сборки в меню "Help" появится новый пункт.
Функция IDERegister отображает универсальный, удобный для разработчиков шаблон:
IOTAWizardService wizServ = (IOTAWizardService) BorlandIDE.GetService(typeof(IOTAWizardService)); |
Большинство сервисных интерфейсов IDE можно получить с помощью метода GetService для класса BorlandIDE. При наличии запрашиваемого интерфейса он вызовет его в любом случае. Заметим, что использование GetService для этой цели необходимо, поскольку оператор "as", обеспечивающий приведение BorlandIDE к типу специального интерфейса в предыдущих версиях IDE, больше не поддерживается. В следующей таблице приведены некоторые интерфейсы, вызываемые с помощью BorlandIDE.GetService.
Интерфейс | Новый для .NET? | Назначение |
IOTAAboutBoxService | Да | Выводит информацию о продукте в информационном окне "About" в IDE (рассматривается далее). |
IOTAActionService | Нет | Открывает, закрывает, сохраняет и загружает файлы и проекты. |
IOTAAddInService | Нет | Загружает в IDE новые дополнительные сборки. |
IOTAAddReferenceDialog | Да | Отображает диалоговое окно "Add Reference" для текущего проекта. |
IOTAAssemblySearchPathService | Да | Перечисляет, добавляет и удаляет пункты из путей поиска сборок. |
IOTAAssemblyUnloadedService | Да | Определяет время выгрузки сборок из IDE. |
IOTABitmapService | Да | Загружает растровое изображение из ресурса Windows в исполняемый файл. |
IOTAComponentInstallService | Да | Перечисляет, добавляет и удаляет компоненты из панели инструментов. |
IOTADotNetObjectInspectorService | Да | Позволяет произвольно выбирать объекты .NET и отображать их свойства в инспекторе объектов IDE Object Inspector. |
IOTAGalleryCategoryManager | Да | Перечисляет, добавляет и удаляет категории галереи. Категориями галереи являются узлы дерева в диалоговых окнах File, New и Other (Repository). |
IOTAIdleNotifier | Да | Получает сообщение о том, что IDE находится в состоянии ожидания. |
IOTAMainMenuService | Да | Последовательно перебирает, добавляет, удаляет и выполняет основные пункты меню. |
IOTAMessageService | Нет | Добавляет или удаляет сообщения и группы сообщений из окна сообщений "Message View". |
IOTAModuleServices | Нет | Получает ссылки на текущую группу проектов, проект, группу файлов (модуль). Открывает, закрывает и создает новые модули. |
IOTAPersistenceManager | Обеспечивает оперативный доступ к документу XML, который содержит пользовательские настройки и настройки по умолчанию для среды разработки (ApplicationSettings.xml). | |
IOTAService | Нет | Предоставляет общую информацию об установочном каталоге IDE, интерфейсе параметров среды и местоположении настроек IDE в реестре и на диске. Кроме того, уведомляет о событиях компиляции, открытии и закрытии файлов, установке/удалении пакетов. |
IOTASplashScreenService | Выводит информацию о продукте на экран-заставку IDE. | |
IOTAWizardService | Нет | Добавляет или удаляет экспертов из IDE. Экспертами могут быть "родные" расширения, эксперты меню, элементы репозитария и т.п. |
Предыдущие версии Borland IDE использовали интерфейсы нотификации для поддержки обратных вызовов во время событий, происходящих в IDE. Новый OTA использует для этой цели события .NET. Это означает, что вместо регистрации нотификатора, способного принимать все 7 событий в старом интерфейсе IOTAIDENotifier, теперь можно регистрировать прием единичного события, например, обратный вызов FileNotification. Например, следующий фрагмент кода показывает, как будут приниматься обратные вызовы FileNotification при открытии и закрытии файлов и других операций с файлами:
public static void IDERegister() { IOTAService ideService = (IOTAService) BorlandIDE.GetService(typeof(IOTAService)); ideService.FileNotification += new FileNotificationHandler(FileEvent); } private static void FileEvent(object sender, FileNotificationEventArgs args) { LogMessage(String.Format("{0} notification for: {1}", args.NotifyCode, args.FileName)); } |
В отличие от IOTAMenuWizard, который всегда помещает новый пункт меню в меню "Help", интерфейс IOTAMainMenuService допускает расширения, позволяющие помещать новые пункты меню в любом месте главного меню и настраивать растровые изображения пунктов меню, горячую клавишу (shortcut) и т.п. Пример кода показывает, как можно это сделать.
protected static void AddMainMenuItem( { ((Bitmap)form.MenuItemBitmap.Image).MakeTransparent(); IOTAMainMenuService menuServ = OTAUtils.MainMenuService; menuItem.Executed += new EventHandler(MenuItemExecuted); int shortcut = Convert.ToInt32(Keys.Z) | OTAUtils.Shift | |
Поскольку для установки растрового изображения пункта меню интерфейсам OTA требуется значение Windows HBITMAP, мы должны привести Image к типу Bitmap и передать результат GetHbitmap(). Метод Bitmap.MakeTransparent берет угловой пиксел растрового изображения и интерпретирует его цвет как прозрачный для всего изображения. В примере кода изображение пункта меню загружается из компонента PictureBox на форме, но его можно загрузить из ресурса с помощью IOTABitmapService. Первым параметром в AddMenuItem является имя пункта меню, которое используется для ссылки на этот пункт. Новый пункт меню размещается соответственно этой ссылке. Имена всех существующих пунктов меню IDE можно получить, используя метод IOTAMainMenuService.GetFirstMenuItem, или с помощью других методов последовательного перебора пунктов меню, таких как IOTAMenuItem.ChildMenuItem.
Каждый интерфейс IOTAMenuItem может иметь горячую клавишу, однако не ясно, какое значение должно быть передано для ее установки. На первый взгляд кажется логичным, что тип этого значения должен принадлежать списку .NET Shortcut, но он не поддерживает большинство горячих клавиш, которые поддерживаются пунктами меню IDE. Вместо этого, горячую клавишу можно интерпретировать как слово, в котором младший байт является значением виртуального кода клавиши в Windows, а старший байт кодирует положение трех клавиш переключения регистра в трех старших разрядах. Чтобы сгенерировать соответствующее значение горячей клавиши, можно использовать оператор "or" в коде клавиши System.Windows.Forms.Keys вместе с любым модификатором Shift, Control и Alt, определенных в OTAUtils. Поскольку .NET OTA пока еще не пересмотрела функциональные возможности привязки клавиш в предыдущих версиях IDE, горячая клавиша нового пункта меню может случайно потеряться во время загрузки или выгрузки пакетов или при повторной инициализации в IDE главного меню. Обойти эту проблему можно с помощью двух служебных методов StartUpdatingMenuShortcut и StopUpdatingMenuShortcut, содержащихся в OTAUtils.cs. Этот прием автоматически возвращает горячую клавишу пункта меню при загрузке и выгрузке пакетов. После выполнения приведенного выше кода получится следующий результат.
Если один из обработчиков событий меню (menu event handlers) сгенерирует не перехваченное исключение, то пользователь может узнать о нем только по сообщению "Exception has been thrown by the target of an invocation" (Исключение было сгенерировано объектом вызова). Это происходит оттого, что внутренний код IDE, обращаясь к объекту .NET, подает сигналы выбираемому пункту меню, а средство вызова событий .NET, перехватывает .NET-исключения и повторно генерирует их с указанным выше сообщением. По этой причине было бы разумно свернуть обработчики событий меню в блок try/catch, послать пользователю сообщение о реальном исключении и затем соответствующим образом обработать это исключение.
При описании файлов IDE разработчики нередко путаются в терминологии OTA. Термин верхнего уровня - группа проектов (.bdsgroup), представляющая собой коллекцию файлов проектов, таких как .bdsproj. В состав проектов может входить любое количество модулей. Модуль состоит из файла или группы файлов и рассматривается как одно целое. Например, модуль ASP.NET может содержать два файла: html-шаблон с расширением .aspx и файл .cs для реализации дополнительной обработки code-behind. В модуль могут входить несколько интерфейсов IOTAEditors, каждый из которых обычно соответствует одному физическому файлу на диске. Среди них могут быть как редакторы IOTASourceEditors для исходных текстовых файлов, так и редакторы других типов. В свою очередь, редакторы исходного кода могут включать один или несколько представлений IOTAEditViews. Несмотря на то, что C#Builder 1.0 не позволяет создавать несколько представлений для одного и того же редактора исходного кода, OTA предоставляет интерфейсы, позволяющие перебирать представления редактора, с тем расчетом, что в следующей версии C#Builder эта функция появится снова. Приведенный в этой статье код показывает, каким образом можно использовать интерфейс IOTAModuleServices для получения текущей группы проектов, проекта, модуля, редактора и редактора исходного кода. Следуя описанным выше инструкциям, установите сборку OTAIntro.dll, затем выберите View и пункт меню OTA Intro Form. Щелкните по кнопке Refresh, чтобы собрать все данные текущего модуля (см. следующую иллюстрацию).
В среде разработки C#Builder палитра компонентов заменена на многоцелевую инструментальную палитру Tool Palette. OTA позволяет определять категории и компоненты, содержащиеся в Tool Palette, и добавляет их в конструкторы форм. Вкладка Components на форме примера кода не только отображает список категорий и компонентов в выбранной категории, но и позволяет создавать компоненты из расширений OTA. На следующей иллюстрации отображен список компонентов с только что созданным компонентом Button.
C#Builder IDE использует конструктор .NET Designer, встроенный в .NET 1.1 SDK, что обеспечивает документированность всех его функциональных возможностей для интерфейса IdesignerHost и доступность связанных с ним интерфейсов. Интерфейс IdesignerHost можно получить, вызвав метод IOTADotNetModule.DesignerHost. IDesignerHost позволяет создавать транзакции элементарных/отменяемых модификаций и реализовать большинство интерфейсов в System.ComponentModel.Design, включая IComponentChangeService, ITypeResolutionService, ISelectionService, INameCreationService, IUIService, IMenuCommandService и т.д. Пример кода использует интерфейс IdesignerHost для получения списка выбранных компонентов и создания компонента с помощью IToolboxUser.ToolPicked.
public static void CreateToolboxItemOnDesigner(ToolboxItem item, IDesignerHost designer) { ISelectionService selection = (ISelectionService) designer.GetService(typeof(ISelectionService)); IComponent selComp = (IComponent)selection.PrimarySelection; IDesigner des = null; if (selComp is IToolboxUser) des = designer.GetDesigner(selComp); else des = designer.GetDesigner(designer.RootComponent); if (des is IToolboxUser) { ((IToolboxUser)des).ToolPicked(item); } } |
Новая интегрированная среда разработки (IDE) предусматривает подключение расширений сторонних фирм и новых компиляторов, например, грядущей версии Delphi 8. IDE предлагает разработчикам расширений новые настраиваемые возможности, такие как размещение описаний продуктов на экране-заставке и в информационном окне "About". Следующий пример кода добавляет в окно среды разработки "About" логотип и связанный с ним текст. Как и при добавлении пункта меню, объект Bitmap в этом изображении интерпретируется как прозрачный и передается в виде значения Windows HBITMAP. Заметим, что значение, возвращаемое из AddPluginInfo, должно быть сохранено, чтобы его можно было использовать для удаления информации о расширении посредством RemovePluginInfo в случае выгрузки этого расширения.
private void AddAbout_Click(object sender, System.EventArgs e) { if (AboutPluginIndex > -1) throw new ApplicationException("You can't add the plugin twice"); IOTAAboutBoxService aboutServ = OTAUtils.AboutBoxService; ((Bitmap)AboutImage.Image).MakeTransparent(); AboutPluginIndex = aboutServ.AddPluginInfo(AboutTitle.Text, AboutDescription.Text, ((Bitmap)AboutImage.Image).GetHbitmap(), AboutUnregistered.Checked, AboutLicenseStatus.Text, AboutSkuName.Text); } |
После выполнения этого кода информационное окно "About" будет иметь примерно такой вид.
Заметим, что аналогичную настройку экрана-заставки можно реализовать с помощью интерфейса IOTASplashScreenService.
Пример кода также иллюстрирует выполнение любых пунктов меню IDE посредством метода OTAUtils.ExecuteMainMenuItemByName. Кнопка "Show About Box" (Показать информационное окно "About") на вкладке "About Box" позволяет разместить и выполнить пункт меню "HelpAboutItem". Таким же образом можно выполнить любой пункт меню IDE, после того как будет найдено его имя с помощью соответствующих интерфейсов IOTAMainMenuService и IOTAMenuItem.
Форма примера кода также содержит вкладку "Message View". Код, запускаемый с помощью этой вкладки, позволяет очистить диалоговое окно "Message View", добавить сообщения и группы сообщений, добавить родительский/дочерний тип вложенных сообщений, щелчок по которым открывает соответствующие файлы и т.п. Функциональные возможности вкладки "Message View" показаны на следующей иллюстрации.
Пример кода также включает большое количество статических утилит класса OTAUtils, содержащихся в файле OTAUtils.cs, которые значительно облегчают разработку проектов OTA. Посредством этих утилит можно легко и безопасно находить сервисные интерфейсы IDE, вызывать интерфейс текущего проекта, выполнять любые пункты меню IDE и т.п. Широко используйте их в своих проектах, создавайте новые методы и присылайте их мне, чтобы я мог добавить их в этот пакет.
Кроме интерфейсов, которые были описаны выше, опытные разработчики также найдут в .NET OTA следующие новые интерфейсы:
Другим преимуществом .NET OTA (по крайней мере, до версии C#Builder 2!) является то, что в результате нескольких успешных редакций IDE, которые позволили значительно расширить функциональные возможности интерфейсов, были исключены сложные иерархии наследования интерфейсов. Например, уже нет больше интерфейса IOTAServices, наследника IOTAServices60, который, в свою очередь, был унаследован от интерфейса IOTAServices50. Теперь существует единственный интерфейс IOTAService, реализующий все прежние методы IOTAServices и дополнения .NET.
Отладка расширений OTA выполняется не так легко, как это можно показаться, поскольку C#Builder 1.0 не поддерживает отладку сборки .NET, если эта сборка была загружена с помощью собственной исполняемой среды Windows, такой как C#Builder IDE (такая попытка приведет к исключению "Unable to scan program's header" (Сканирование заголовка программы невозможно)). Обойти это затруднение и выполнить отладку можно разными способами, например, путем подключения к уже работающему экземпляру C#Builder, последовательно выбрав пункты меню Run и Attach to Process (подробности см. в Readme.txt), или с помощью журнального файла (log file) или сообщений, посылаемых посредством IOTAMessageService и т.п. Чтобы отладить код инициализации для своего расширения, разместите вызов MessageBox.Show в первой строке процедуры IDERegister и прикрепите к новому процессу IDE, когда IDE будет ожидать подтверждения диалога "MessageBox".
Ниже приведен список полезных возможностей, а также некоторых возможностей старого OTA, которые пока еще не поддерживаются .NET OTA.
Пример кода, используемый в этой статье, можно найти в CodeCentral под идентификатором ID #20287.
Эрик Берри (Erik Berry) – независимый консультант и разработчик программного обеспечения из Сент-Луиса, штат Миссури, работающий в корпорации Oasis Digital Solutions. Эрик Берри является руководителем проекта по разработке пакета инструментальных средств GExperts для Delphi и C++Builder, участвовал в проектировании и внедрении Open Tools API для C#Builder 1.0. Он также ведет web-страницу Open Tools API FAQ.
Дополнительная информация
За дополнительной информацией обращайтесь в компанию Interface Ltd.
INTERFACE Ltd. |
|