Написання Plugin “ов для Internet Explorer, Різне, Інтернет-технології, статті

Всім пам’ятні звинувачення на адресу Microsoft в тому, що включення браузера Internet Explorer до складу операційної системи Windows неприпустимо. Відповіддю корпорації було те, що браузер є невід’ємною частиною системи. Тепер ми можемо сказати навіть більше – Internet Explorer як єдине додаток не існує. Це набір компонентів, які збираються в єдине ціле тільки при запуску програми. Зараз ми спробуємо включити в цей стрункий ряд компонентів свій, щоб він теж став невід’ємною частиною, ну якщо не операційної системи, то конкретної копії браузера точно.


Що ми будемо робити?


Що являє собою плагін для 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 повинна з’явитися кнопка, а в меню «Сервис» рядок, що запускає наш плагін.
Сподіваюся, що написання плагінів для самого популярного у світі браузера не здалося вам важким. Якщо так, то у вас напевно з’явилося бажання поекспериментувати з компонентами, що розширюють функціональність браузера. В цьому випадку я можу вважати, що мета, до якої я прагнув при написанні цієї статті, досягнута.




Залишити коментар


Залишати коментарі можуть тільки зареєстровані користувачі.


Якщо ви не є зареєстрованим користувачем, то вам необхідно зареєструватися. Реєстрація безкоштовна. Якщо ви вже зареєстровані на CodeNet, то вам необхідно ввести логін і пароль у верхній (Alt-U) частині сторінки.


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


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

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

Ваш отзыв

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

*

*