The entire process of creating an Addon in Internet Explorer is explained in this article. The Addon will place an ad in every page opened by the browser. The ad will be made in HTML/CSS and will be written to the page's DOM when the page is loaded. Coding is done in C++ is to remove the .NET dependency. The final goal of this sample is to write a with absolute position with a background image and another acting as a close button.
Introduction
This article will cover the entire process of creating an Addon in Internet Explorer. The Addon will place an ad in every page opened by the browser. The ad will be made in HTML/CSS and will be written to the page's DOM when the page is loaded. The goal to make it in C++ is to remove the .NET dependency in order to reach a wider audience. That made sense to me when my C# Addon was installed in 40k PCs and some of them didn't have .NET installed. I decided to write this article after spending a lot of time searching on the internet and finding almost nothing. I hope that will be useful for you. The final goal of this sample is to write a <div>
with absolute position with a background image and another <div>
acting as a close button. So it will be required to insert some JavaScript code as well.
The only way to write Addons for Internet Explorer is through Browser Helper Objects (also called BHOs). They are COM components that act as Internet Explorer plug-ins. BHOs can be used for customizing Internet Explorer to any extent: from user-interface modifications to web filters to download managers.
This article is mainly based on this one. I strongly recommend reading it in order to understand this one in detail. Subjects such as BHO installation aren't covered here.
Using the Code
Most of the code that is on the sample is explained in the referenced article. The only part which is really important is the CEventSink
class. This class is the one that captures all events fired by the browser. The event which I want to capture is the OnDocumentComplete
because I will write the HTML content to the DOM when the page is loaded. Here is the function that captures the events.
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,
WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
VARIANT v[5]; int n;
bool b;
PVOID pv;
LONG lbound,ubound,sz;
if(!IsEqualIID(riid,IID_NULL)) return DISP_E_UNKNOWNINTERFACE; for(n=0;n<5;n++) VariantInit(&v[n]);
if(dispIdMember==DISPID_BEFORENAVIGATE2) { VariantChangeType(&v[0],&pDispParams->rgvarg[5],0,VT_BSTR); VariantChangeType(&v[1],&pDispParams->rgvarg[4],0,VT_I4); VariantChangeType(&v[2],&pDispParams->rgvarg[3],0,VT_BSTR); VariantChangeType(&v[3],&pDispParams->rgvarg[2],0,VT_UI1|VT_ARRAY); VariantChangeType(&v[4],&pDispParams->rgvarg[1],0,VT_BSTR); if(v[3].vt!=VT_EMPTY) {
SafeArrayGetLBound(v[3].parray,0,&lbound);
SafeArrayGetUBound(v[3].parray,0,&ubound);
sz=ubound-lbound+1;
SafeArrayAccessData(v[3].parray,&pv);
} else {
sz=0;
pv=NULL;
}
b=Event_BeforeNavigate2((LPOLESTR)v[0].bstrVal,v[1].lVal,
(LPOLESTR)v[2].bstrVal,(PUCHAR)pv,sz,(LPOLESTR)v[4].bstrVal,
((*(pDispParams->rgvarg[0].pboolVal))!=VARIANT_FALSE));
if(v[3].vt!=VT_EMPTY) SafeArrayUnaccessData(v[3].parray);
if(b) *(pDispParams->rgvarg[0].pboolVal)=VARIANT_TRUE;
else *(pDispParams->rgvarg[0].pboolVal)=VARIANT_FALSE;
}
if(dispIdMember==DISPID_DOCUMENTCOMPLETE){
VariantChangeType(&v[0],&pDispParams->rgvarg[0],0,VT_BSTR); OnDocumentComplete(NULL,(BSTR)v[0].bstrVal);
}
for(n=0;n<5;n++) VariantClear(&v[n]);
return S_OK;
}
So until now, I managed to capture the OnDocumentComplete
event. Now the "magic" is performed in the OnDocumentComplete
function. Here is the code:
void STDMETHODCALLTYPE CEventSink::OnDocumentComplete(IDispatch *pDisp, BSTR url)
{
if(UrlSite != NULL){
BSTR result = wcsstr(UrlSite, url);
if(result != NULL)
return;
}
HRESULT hr = S_OK;
IHTMLDocument2 *pDocument;
pSite->get_Document((IDispatch**)&pDocument);
CComQIPtr<IHTMLWindow2> pWindow;
pDocument->get_parentWindow(&pWindow);
BSTR bstrT1 = NULL;
bstrT1 = SysAllocString (L"function hidediv()
{document.getElementById(\"absoluteID123\").style.visibility = \"hidden\";}");
BSTR bstrT2 = NULL;
bstrT2 = SysAllocString (L"javascript");
CComVariant v;
hr = pWindow->execScript(bstrT1, bstrT2, &v);
IHTMLElement *pElementBody;
pDocument->get_body((IHTMLElement**)&pElementBody);
if(CheckInsert(pElementBody))
return;
BSTR bstrBegin = NULL;
bstrBegin = SysAllocString ( L"afterBegin" );
BSTR bstrL2 = NULL;
bstrL2 = SysAllocString ( L"<div id=\"absoluteID123\" style=\"position:absolute;
top:0;right:0;padding: 20px;z-index:2000000\">" );
BSTR bstrL3 = NULL;
bstrL3 = SysAllocString ( L"<table><tr><td>");
BSTR bstrL4 = NULL;
bstrL4 = SysAllocString ( L"<div id=\"div123456\" style=\"z-index:2000002;
width:307px;height:158px;background-image:
url(http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);\"/>");
BSTR bstrL5 = NULL;
bstrL5 = SysAllocString ( L"<div id=\"divButton123\" onclick=\"hidediv()\"
style=\"z-index:2000003; position: relative;
left: 279px;top: 6px; width:23px;height:24px;\"/>
</div></div>");
BSTR bstrL6 = NULL;
bstrL6 = SysAllocString ( L"</td></tr></table></div>");
BSTR bstrFinal = NULL;
bstrFinal = Concat(bstrFinal,bstrL2);
bstrFinal = Concat(bstrFinal,bstrL3);
bstrFinal = Concat(bstrFinal,bstrL4);
bstrFinal = Concat(bstrFinal,bstrL5);
bstrFinal = Concat(bstrFinal,bstrL6);
SysFreeString(bstrL2);
SysFreeString(bstrL3);
SysFreeString(bstrL4);
SysFreeString(bstrL5);
pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);
SysFreeString(bstrBegin);
SysFreeString(bstrFinal);
UrlSite = Concat(UrlSite,url);
}
About this function, there are a few details that I want to explain. The first one is that is that first, I added the JavaScript code with the HTML code and placed the defer attribute on the script
tag. I had to change that when I found out that it didn’t work in versions above Internet Explorer 8. The other detail is that in certain pages using frames, the OnDocumentComplete
function is fired more than once so you need a way of telling whether or not you have inserted the Addon, the CheckInsert
function on the sample does that.
Points of Interest
I was very frustrated when studying the MSDN example of removing images in the browser using a C++ BHO. I couldn’t understand why they didn’t have a downloadable sample and I spent a lot of time to get the code integrated with the article on which this one is based. I also made a nice installer using NSIS. If you need it, just ask for it in the comments below.
History
- 18th July, 2014: First version