Dajbych.net


Jak vytvářet doplňky pro Internet Explorer, díl 1

, 9 minut čtení

In­ter­net Ex­plorer lze rozšířit mnoha způ­soby. Je možné přidá­vat položky do nabídek, vytvářet vlastní pan­ely, samostatná tlačítka na panel příkazů nebo vytvořit akcel­erá­tor. Pro­tože se do­plňky, až na akcel­erá­tory, pro­gra­mují v C++, ukážeme si, jak si práci po­mocí .NET kni­hoven zjedn­odušit. Využi­jeme přitom rozšíření CLI jazyka C++, které dokáže zpřís­tup­nit celý .NET. Část do­plňku tedy může být nap­sána v jazyce C#.

Vytvoření do­plňku In­ter­net Ex­ploreru rozhodně není in­tu­i­tivní záleži­tost. Když jsem před rokem potře­bo­val do­pl­něk pro IE vytvořit, návod podobný to­muto jsem hledal marně. Do­plňky pro In­ter­net Ex­plorer pro jeho vývo­jový tým ne­j­sou v současné době pri­orita, takže je­jich vytváření není tak snadné, jak by mohlo být. Přesto dokážou být velice užitečné.

Ukažme si proto, jak vytvořit jedn­oduchý do­pl­něk – tlačítko pro příka­zový panel, který vypíše ti­t­ulek a adresu stránky. Na tomto prostém přík­ladu osvětlíme prin­cip vytváření do­plňku, jeho reg­is­traci a vytváření in­stalá­toru.

Projekt

Ne­jprve je potřeba založit ATL pro­jekt. Ten naleznete v kat­e­gorii Vi­sual C++. Pokud chcete, aby vám rov­nou fun­go­valy ukázky zdro­jového kódu bez pře­j­men­ovávání tříd a rozhraní, nazvěte ho MyIeAd­don.

Doplňky

V průvodci je potřeba zaškrt­nout Al­low merg­ing of proxy/stub code, aby se vy­gen­eroval jen je­den pro­jekt.

Doplňky

Prvním krokem je přidání třídy, která tlačítko reprezen­tuje. Z místní nabídky pro­jektu vy­berte Add a poté Class. Následně zvolte položku ATL Sim­ple Ob­ject.

Doplňky

Třídu vhodně po­j­menu­jte. Já jsem zvo­lil název My­But­ton, aby bylo z ukázek zdro­jového kódu hned jasné, o kterou třídu se jedná.

Doplňky

Pod­poru pro náh­led či hledání nechte v průvodci vyp­nutou. Volbu Ag­gre­ga­tion nas­tavte na No a zaškrt­něte IOb­jectWith­Site.

Doplňky

Tak to byla ta le­hčí část. Nyní je potřeba přidat něko­lik řádků kódu do hlav­ičky třídy tlačítka. Tím se defin­uje rozhraní pro jeho zo­brazení v Inter­net Exploreru.

// MyButton.h : Declaration of the CMyButton #pragma once #include "resource.h" // main symbols #include <shlguid.h> // IID_IWebBrowser2, DIID_DWebBrowserEvents2, etc. #include <exdispid.h> // DISPID_DOCUMENTCOMPLETE, etc. #include <mshtml.h> // DOM interfaces #include "MyIeButton_i.h" #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) #error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." #endif using namespace ATL; // CMyButton class ATL_NO_VTABLE CMyButton : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMyButton, &CLSID_MyButton>, public IObjectWithSiteImpl<CMyButton>, public IDispatchImpl<IMyButton, &IID_IMyButton, &LIBID_MyIeButtonLib, 1, 0>, public IDispEventImpl<1, CMyButton, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>, public IDispEventImpl<0, CMyButton, &DIID_HTMLDocumentEvents2, &LIBID_MSHTML, 4, 0>, public IOleCommandTarget { public: CMyButton() { } DECLARE_REGISTRY_RESOURCEID(IDR_MYBUTTON) DECLARE_NOT_AGGREGATABLE(CMyButton) BEGIN_SINK_MAP(CMyButton) SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) END_SINK_MAP() BEGIN_COM_MAP(CMyButton) COM_INTERFACE_ENTRY(IMyButton) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IObjectWithSite) COM_INTERFACE_ENTRY(IOleCommandTarget) END_COM_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } public: // IObjectWithSite STDMETHOD(SetSite)(IUnknown *pUnkSite); // IOleCommandTarget STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut); STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText); // DWebBrowserEvents2 void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL); private: CComPtr<IWebBrowser2> m_spWebBrowser; BOOL m_fAdvised; CComQIPtr<IOleCommandTarget, &IID_IOleCommandTarget> m_spTarget; }; OBJECT_ENTRY_AUTO(__uuidof(MyButton), CMyButton) Samotná implementace metod může vypadat například takto: // MyButton.cpp : Implementation of CMyButton #include "stdafx.h" #include "MyButton.h" // CMyButton void STDMETHODCALLTYPE CMyButton::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL) { } STDMETHODIMP CMyButton::SetSite(IUnknown *pUnkSite) { if (pUnkSite != NULL) { // Cache the pointer to IWebBrowser2 CComQIPtr<IServiceProvider> sp = pUnkSite; HRESULT hr = sp->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&m_spWebBrowser); hr = sp->QueryInterface(IID_IOleCommandTarget, (void**)&m_spTarget); if (SUCCEEDED(hr)) { // Register to sink events from DWebBrowserEvents2. hr = IDispEventImpl::DispEventAdvise(m_spWebBrowser); if (SUCCEEDED(hr)) { m_fAdvised = TRUE; } } } else { // Unregister event sink. if (m_fAdvised) { IDispEventImpl::DispEventUnadvise(m_spWebBrowser); m_fAdvised = FALSE; } // Release pointer m_spWebBrowser.Release(); m_spTarget.Release(); } // Return base implementation return IObjectWithSiteImpl<CMyButton>::SetSite(pUnkSite); } STDMETHODIMP CMyButton::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut) { if (m_spWebBrowser != NULL) { BSTR url; BSTR title; m_spWebBrowser->get_LocationURL(&url); m_spWebBrowser->get_LocationName(&title); MessageBox(NULL, url, title, 0); ::SysFreeString(url); ::SysFreeString(title); return S_OK; } else { MessageBox(NULL, _T("No Web browser pointer"), _T("Oops"), 0); return E_ABORT; } } STDMETHODIMP CMyButton::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP; if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons)) { for (ULONG i = 0; i < cCmds; i++) { if (m_spWebBrowser) { // By default, we'll support all commands prgCmds[i].cmdf = OLECMDF_ENABLED | OLECMDF_SUPPORTED; } else { // If we wanted to latch the button down, we could do this: prgCmds[i].cmdf |= OLECMDF_LATCHED; } } hr = S_OK; } return hr; }

Me­toda Exec má na starosti ob­s­luhu stisknutí tlačítka. Me­toda QueryS­ta­tus určuje, je-li tlačítko po­v­olené, či zakázané. Me­toda On­Doc­u­ment­Com­plete je volána vždy po načtení stránky.

Registrace

Kni­hovna se musí zareg­istrovat, čímž se vlastně nain­staluje do In­ter­net Ex­ploreru. K tomu je potřeba ne­jprve zjis­tit patřičná GUID ob­jektů v kni­hovně. Ty se nacházejí v souboru MyIeAd­don.idl:

interface IMyButton : IDispatch{ }; [ uuid(78F4AA07-8BAD-4D7E-AA30-D3726A96C3FD), version(1.0), ] library MyIeButtonLib { importlib("stdole2.tlb"); [ uuid(9FCA1565-739D-4741-8957-D5A7957AB6F4) ] coclass MyButton { [default] interface IMyButton; }; };

Do souboru MyIeAd­don.rgs vložte tento kód a nahraďte v něm použitá GUID za ty vaše. Tím se zareg­istruje rozšíření proh­lížeče.

HKLM { NoRemove SOFTWARE { NoRemove Microsoft { NoRemove 'Internet Explorer' { NoRemove Extensions { ForceRemove '{A3278C3B-DA28-4E8E-924D-E1676D7BF4BE}' = s 'MyButton' { val 'Default Visible' = s 'yes' val 'ButtonText' = s 'My IE Button' val 'CLSID' = s '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}' val 'ClsidExtension' = s '{A3278C3B-DA28-4E8E-924D-E1676D7BF4BE}' val 'Icon' = s '%%ProgramFiles(x86)%%\MyIeAddon\icon.ico' val 'HotIcon' = s '%%ProgramFiles(x86)%%\MyIeAddon\icon.ico' } } } } } }

Tlačítko má pouze jedi­nou ikonu o rozměrech 16 × 16 px a nelze jí dy­nam­icky měnit. Položky Icon a HotI­con defin­ují její umístění. V cestě je možné uvést sys­té­movou proměn­nou. Není to sice na MSDN nikde doku­men­továno, ale fun­guje to. Využi­jete to pro in­sta­laci x86 do­plňku na x64 sys­témech. Jak proměn­nou při­dat během in­sta­lace na starších sys­témech, kde není defi­nována, ukážu později.

Jsou ještě další typy do­plňků. Ve­dle rozšíření proh­lížeče ex­is­tuje ještě ob­jekt po­moc­níka proh­lížeče. Zatímco rozšíření proh­lížeče je do paměti načteno až když uži­va­tel na tačítko klikne (proto ne­jde měnit ikona), po­moc­ník proh­lížeče se načítá při jeho spuštění. Třída pro zpra­cování událostí jako například načtení stránky je tedy lepší reg­istrovat spíše jako ob­jekt po­moc­níka proh­lížeče:

HKLM { NoRemove SOFTWARE { NoRemove Microsoft { NoRemove Windows { NoRemove CurrentVersion { NoRemove Explorer { NoRemove 'Browser Helper Objects' { ForceRemove '{ 9FCA1565-739D-4741-8957-D5A7957AB6F4}' = s ' MyIeAddon' { val 'NoExplorer' = d '1' } } } } } } } }

Každý typ do­plňku musí im­ple­men­to­vat trošku jiné rozhrní. Připravil jsem však ukázku zdro­jového kódu tak, že im­ple­men­tuje rozhraní obě. Typ do­plňku tedy určuje jen to, jakým způ­sobem se zareg­istruje. Je možné reg­istrovat jednu třídu jako rozšíření proh­lížeče i ob­jekt po­moc­níka proh­lížeče.

V něk­terých pří­padech se stává, že se reg­is­trace po buildu nepovede. Naštěstí je v přímo popisu chyby pop­sáno, jak jí vyřešit. Stačí ve vlast­nos­tech pro­jektu po­volit Per-user Redi­rec­tion.

Doplňky

Funkce

Samotná funk­cion­alita může být nap­saná v jazyce C++/CLI, který do­voluje použít kni­hovny plat­formy .NET. To sice mírně zvýší dobu potřeb­nou pro spuštění In­ter­net Ex­ploreru, na druhou stranu není nutné učit se MFC. Úvod do C++/CLI světa sep­sal Jakub Čer­mák.

Doplňky

Rozšíření CLI zavádí jazykové kon­strukce nutné pro pro­gramování v .NET, napřík­lad man­aged třídy. Je­jich in­s­tance se vytváří příkazem gc­new. Ukazuje se na ně zvlášt­ními poin­t­ery a o je­jich uvolňování z paměti se stará garbage col­lec­tor.

Co všechno lze se stránkou dělat bude před­mětem dalších dílů. Pro před­s­tavu jen uvedu, že jde napřík­lad upravo­vat DOM stránky nebo číst cook­ies proh­lížeče.

Instalátor

K vytvoření in­stalá­toru se ne­jvíce hodí In­stall­Shield LE, který lze do Vi­sual Stu­dia doin­stalo­vat. Postará se o zkopírování souborů do správného adresáře a další důležité pro­ce­dury. Je schopný v pří­padě potřeby stáh­nout a nain­stalo­vat .NET Frame­work.

Doplňky

Vytvoření in­stalá­toru není úplně pří­močaré. Prob­lém způ­sobují dvě složky Pro­gram Files na x64 sys­témech. V sou­borech k in­sta­laci je potřeba ve vlast­nos­tech in­stalo­vané kni­hovny nas­tavit samočin­nou reg­is­traci. Ta během in­sta­lace přes pro­gram regsvr32 reg­istruje kni­hovnu ste­jně jako Vi­sual Studio po buildu.

Doplňky

Pokud používáte C++/CLI, zaškrt­něte v Re­dis­tributa­bles Mi­crosoft .NET Frame­work Full (Web Down­load). Tím in­stalá­tor doin­staluje .NET Frame­work 4, pokud ještě v sys­tému není. Dále v položce Cum­s­tom Ac­tions přide­jte do události Af­ter Reg­is­ter Prod­uct nový VB­Script.

Doplňky

V něm se může vytvořit sys­té­mová proměnná Pro­gram­Files(x86), pokud v sys­tému ještě defi­no­vaná neni. To ob­s­tará jedn­oduchý skript ve Vi­sual Ba­sicu:

Set wshShell = CreateObject( "WScript.Shell" ) If wshShell.ExpandEnvironmentStrings( "%ProgramFiles(x86)%" ) = "%ProgramFiles(x86)%" Then Set wshSystemEnv = wshShell.Environment( "SYSTEM" ) wshSystemEnv( "ProgramFiles(x86)" ) = "C:\Program Files" End If

Ak­tu­al­izace do­plňku se provádí opě­tov­nou in­sta­lací novější verze. In­ter­net Ex­plorer během ní nesmí být spuštěn.

V dalším díle si ukážeme, jak číst cookie proh­lížeče a vytvářet tak do­plňky, které mo­hou ně­jakým způ­sobem zpra­co­vat s pro­filem uži­vatele. Ten se tak neb­ude muset přih­lašo­vat zvlášť na we­bové stránce a zvlášť do do­plňku proh­lížeče.

Zdro­jový kód přík­ladu.

Článek byl sep­sán pro Zdro­ják.