Що ми будемо робити?, Інтернет і мережа, C / C + +, статті

Що являє собою плагін для Internet Explorer? Це звичайний
внутріпроцессний (In Process) COM-сервер (тобто DLL-файл), який містить
об’єкт, який реалізує як мінімум 2 інтерфейсу: IOleCommandTarget і
IObjectWithSite. Крім того, наш DLL-файл повинен експортувати не менше 2
функцій: DllGetClassObject і DllCanUnloadNow. Думаю, їх призначення всім
відомо.


Наш плагін буде дуже простим. Він буде зберігати в результуючий файл все
посилання зі сторінки, які вказують на файли з заданими в. ini-файлі
розширеннями. Такий плагін може бути корисний, наприклад, при створенні списків
завантажуваних файлів для download-менеджерів. Шукати і зберігати заслання він буде
при натисканні на кнопку, яку ми додамо на панель інструментів браузера, або
при виборі відповідного пункту в меню Сервіс. Кнопку і пункт меню ми будемо
робити доступними (enabled) тільки в тому випадку, якщо в браузері відкритий файл з
розширенням. htm або. html (це ми зробимо просто для демонстрації такої
можливості).


Як це працює?


Тепер, коли ми визначилися, що будемо писати, саме час дізнатися, як це
буде працювати. А працювати це буде наступним чином: насамперед, браузер
завантажує нашу бібліотеку, це відбувається разом із завантаженням самого IE. Потім,
після першого натискання на кнопку, він викликає експортовану функцію
DllGetClassObject і запитує в неї покажчик на інтерфейс IClassFactory. З
отриманого інтерфейсу він викликає метод CreateInstance і запитує в нього
інтерфейс 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 »; це значно прискорює роботу). Через цю змінну ми завжди будемо
мати доступ до всіх наданих браузером інтерфейсів. Код цього методу
вигладить наступним чином:

STDMETHODIMP IMyIEExtention::SetSite(IUnknown *pUnkSite)
{
if (!pUnkSite)
{
if (m_pWebBrowser2.GetInterfacePtr())
m_pWebBrowser2.Release();
return S_OK;
}

IServiceProviderPtr pServProv(pUnkSite);
return pServProv->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2, (void**)&m_pWebBrowser2);
}


На початку я перевіряю, чи не хоче IE сказати мені цим викликом, що відбувається
завершення його роботи, і я повинен звільнити його інтерфейси. Далі –
цікавіше. Я звертаюсь інтерфейс IWebBrowser2, але не як завжди, через виклик
QueryInterface, а за допомогою виклику методу QueryService попередньо
отриманого інтерфейсу IServiceProvider. Навіщо потрібні такі дивні маніпуляції
для вирішення, здавалося б, стандартної задачі?


Інтерфейс IServiceProvider призначений для використання в наступних
ситуаціях. Припустимо, існує якесь додаток-контейнер, яке
використовує кілька COM-серверів. У кожного з них, природно, є доступ до
інтерфейсам контейнера (за допомогою IObjectWithSite:: SetSite, наприклад). Але ось
якомусь із COM-серверів було потрібно отримати доступ до інтерфейсів іншого
COM-сервера, також міститься в контейнері.


Як же йому вирішити це завдання? Адже стандартними засобами він до іншого
сервера ніяк не добереться, оскільки контейнер, у відповідності з ідеологією
COM, не надає доступ до інтерфейсів містяться в ньому об’єктів
безпосередньо через виклики QueryInterface своїх інтерфейсів.


Для вирішення таких завдань якраз і призначений інтерфейс IServiceProvider.
Його єдиний метод – QueryService – відрізняється від QueryInterface одним
параметром – ідентифікатором сервісу. Фактично це ідентифікатор одного з
COM-компонентів, що використовуються додатком-контейнером. І коли COM-сервер хоче
отримати інтерфейс іншого сервера, використовуваного тим же клієнтом, він просто
викликає вищезгаданий метод з відповідним ідентифікатором сервісу.


Клієнт же, в свою чергу, просто визначає, який з містяться в ньому
компонентів відповідає переданим ідентифікатором і викликає його
QueryInterface.
Повертаючись до нашого завдання, легко помітити, що тут
аналогічна ситуація. Internet Explorer є зоопарк компонентів,
де наш COM-сервер (тобто плагін) – один з вихованців. Тому нам і доводиться
використовувати вищеописану техніку для отримання доступу до інтерфейсів іншого
компонента (яким, у нашому прикладі, є WebBrowser Control).
Наступним
в черзі на реалізацію у нас стоїть метод QueryStatus інтерфейсу
IOleCommandTarget. Його текст виглядає наступним чином:

STDMETHODIMP IMyIEExtention::QueryStatus(const GUID *pCmdGroup,
ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
{
if (!prgCmds)
return E_POINTER;
ASSERT(cCmds == 1);
if (!cCmds)
return E_UNEXPECTED;

BSTR url;
HRESULT hRes=S_OK;
hRes=m_pWebBrowser2->get_LocationURL(&url);
CHECK_COM_RESULT(hRes)
bstr_t pszUrl(url, false);

LPCTSTR pExt=(LPCTSTR)pszUrl+pszUrl.length()-5;
if (!_tcsicmp(pExt, _T(".html")) || !_tcsicmp(pExt+1, _T(".htm")))
prgCmds[0].cmdf=OLECMDF_ENABLED;
else
prgCmds[0].cmdf=OLECMDF_SUPPORTED;

return S_OK;
}


На початку необхідно переконатися у коректності переданих даних. Потім ми
просто запитуємо поточний URL і, якщо його останні символи. htm або. html,
робимо кнопку і пункт меню доступними, і недоступними в іншому випадку.
Слід зауважити, що в цей метод завжди повинен передаватися тільки один
елемент у масиві prgCmds, т.к. ми відповідаємо лише за одну кнопку і пункт
меню.


Тепер ми впритул підійшли до реалізації основної функції нашого плагіна. В
ній ми будемо шукати містяться на сторінці посилання і зберігати їх у файлі. Код
цієї функції я не буду наводити тут, тому що він простий і не має прямого
відношення до написання плагінів. З ним можна ознайомитися в початковому тексті
демонстраційного додатка, який можна завантажити з сайту журналу
www.programme.ru.


Як це підключити?


Тепер залишився останній штрих – реєстрація нашого компонента в реєстрі. В
Передусім необхідно коректно зареєструвати плагін як COM-сервер. Я не
буду описувати цю процедуру тут, оскільки це лежить за рамками моєї статті,
та й інформації на цю тему чимало. Зупинимося докладніше на реєстрації нашої
DLL в якості плагіна для Internet Explorer. Для цього необхідно створити
наступний ключ в реєстрі:

  SoftwareMicrosoftInternet ExplorerExtensions <ваш GUID>

Як може виступати або HKEY_CURRENT_USER (у цьому
випадку плагін буде доступний тільки поточному користувачеві), або
HKEY_LOCAL_MACHINE (плагін буде доступний всім користувачам). Тепер у ньому
необхідно створити такі параметри:



Файл, на який вказує параметр HotIcon, повинен містити такі
кольорові індикатори:



Другий файл (відповідний параметру Icon) повинен містити значки в
відтінках сірого. Параметри цих значків наступні:



Взагалі-то і ці значки можуть бути кольоровими, але в такому випадку вони не будуть
відповідати загальному стилю оформлення панелей інструментів Internet Explorer.
Детальніше про стиль, в якому мають бути вирішені ці кнопки можна прочитати тут
(http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/ext/tutorials/button.asp).


Виконання операцій по додаванню інформації до реєстру логічно покласти на
функцію DllRegisterServer, експортовану нашої DLL. Саме так зроблено в
прикладі до цієї статті. Також в демонстраційному додатку реалізована функція
DllUnregisterServer, що видаляє всю інформацію про плагіні з реєстру.


Що в результаті?


Тепер, якщо ви слідували наведеними вище дій, на панелі інструментів
Internet Explorer повинна з’явитися кнопка, а в меню «Сервіс» рядок, що запускає
наш плагін.
Сподіваюся, що написання плагінів для самого популярного у світі
браузера не здалося вам важким. Якщо так, то у вас напевно з’явилося
бажання поекспериментувати з компонентами, що розширюють функціональність
браузера. У цьому випадку я можу вважати, що мета, до якої я прагнув при
написанні цієї статті, досягнута.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*