Jenter Алекс
Шрифт:
СТАТЬЯ
Написание Plugin'ов для Internet Explorer
Автор: Борис Гулай aka BoresExpress
Источник: журнал "Программист" №1 за 2002
Исходные тексты примера – 11 KB
Всем памятны обвинения в адрес Microsoft в том, что включение браузера Microsoft Internet Explorer в состав операционной системы Windows недопустимо. Ответом корпорации было то, что браузер является неотъемлемой частью системы. Теперь мы можем сказать даже больше – Internet Explorer как единое приложение не существует. Это набор компонентов, которые собираются в единое целое только при запуске приложения. Сейчас мы попробуем включить в этот стройный ряд компонентов свой, чтобы он тоже стал неотъемлемой частью, ну если не операционной системы, то конкретной копии браузера точно.
Архитектура Internet Explorer
Что мы будем делать?
Что же представляет собой плагин для Internet Explorer? Это обычный внутрипроцессный (In Process) COM-сервер (т.е. DLL-файл), который содержит объект, реализующий как минимум 2 интерфейса: IOleCommandTarget и IObjectWithSite. Кроме того, наш dll-файл должен экспортировать не менее 2 функций: DllGetClassObject и DllCanUnloadNow. Думаю, их назначение всем известно.
Наш плагин будет очень простым. Он будет сохранять все ссылки страницы, которые указывают на файлы с заданными в .ini-файле расширениями в результирующий файл. Такой плагин может быть полезен, например, при создании списков закачиваемых файлов для download-менеджеров. Искать и сохранять ссылки он будет при нажатии на кнопку, которую мы добавим на панель инструментов браузера, или при выборе соответствующего пункта в меню 'Сервис'. А кнопку и пункт меню мы будем делать доступными (enabled) только в том случае, если в браузере открыт файл с расширением .htm или .html (это мы сделаем просто для демонстрации такой возможности).
Теперь, когда мы определились, что будем писать, самое время узнать, как это будет работать. А работать это будет следующим образом.
Прежде всего, браузер загружает нашу библиотеку, это происходит вместе с загрузкой самого IE. Затем, после первого нажатия на кнопку (!), он вызывает экспортируемую функцию DllGetClassObject и запрашивает у неё указатель на интерфейс IClassFactory. Далее, из полученного интерфейса он вызвает метод CreateInstace и запрашивает у него интерфейс IUnknown. Это должен быть IUnknown компонента, который реализует и IOleCommandTarget и IObjectWithSite.
Два вышеназванных интерфейса должны быть реализованы именно в одном компоненте. Internet Explorer будет запрашивать один через QueryInterface другого. Поэтому реализовать их отдельно нет никакой возможности.
Такое поведение контейнера выглядит логичным, если принять во внимание то, зачем компоненту интерфейс IObjectWithSite. Через его метод SetSite браузер передаёт указатель на интерфейс, через который можно добраться до IWebBrowser – основного интерфейса WebBrowser Control. Это может потребоваться компоненту, при обработке нажатия на кнопку или выбора пункта меню, если он захочет узнать, в каком контексте произошло это событие. Поэтому совершенно логично, что IObjectWithSite должен реализовывать тот же компонент, который обрабатывает нажатие на кнопку.
После того, как произошло первое нажатие на кнопку, Internet Explorer вызывает метод SetSite интерфейса IObjectWithSite и передаёт в него IUnknown объекта, реализующего интерфейс IShellBrowser. Хочу обратить Ваше внимание, что вызов вышеназванного метода происходит только один раз.
Затем, в ответ на нажатие кнопки, вызывается метод IOleCommandTarget::Exec, в котором и происходит обработка события.
После вызова IObjectWithSite::SetSite IE периодически вызывает метод IOleCommandTarget::QueryStatus, где плагин может, при необходимости, изменить статус своей кнопки и пункта меню (enabled/disabled).
При завершении своей работы браузер вызывает IObjectWithSite::SetSite со значением NULL в качестве единственного аргумента, что говорит плагину о необходимости освободить (Release) сохранённый после первого вызова SetSite интерфейс браузера (если он его сохранял, конечно). Затем IE освобождает все интерфейсы плагина и при положительном ответе функции DllCanUnloadNow выгружает плагин.
Так выглядят, в общих чертах, то, что нам придётся запрограммировать.
После знакомства с механизмом интеграции плагинов в Internet Explorer, мы можем приступать к написанию кода. Я предполагаю, что читатель знаком с основами COM, поэтому не буду описывать создание COM-сервера и добавление в него компонентов. А сразу перейду к самому интересному – реализации методов интерфейсов, которые необходимы плагину для полноценного функционирования.
Следует сразу (пока Вы ещё не успели начать работу) сказать, что метод IObjectWithSite::GetSite в реализации не нуждается (хотя в примере он и реализован), т.к. браузер его никогда не вызывает (он ведь всегда знает, какая страница в нём открыта).
Начнём мы с самого простого, а именно с метода IObjectWithSite::SetSite. Для начала добавим в объявление объекта переменную типа IWebBrowser2Ptr (я предпочитаю использовать то, что в MSDN называется compiler COM support classes; это значительно ускоряет работу). Через эту переменную мы всегда будем иметь доступ ко всем предоставляемым браузером интерфейсам.
Код этого метода выгладит следующим образом: