Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Writing HTML Content to a Webpage's DOM with a C++ BHO

4.78/5 (9 votes)
18 Jul 2014CPOL3 min read 37.4K   1.3K  
How to write your own HTML content into any webpage through BHOs in C++
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.

C++
// This is called by Internet Explorer to notify us of events
// Full documentation about all the events supported by DWebBrowserEvents2 
// can be found at http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx
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]; // Used to hold converted event parameters before passing them 
               // onto the event handling method
 int n;
 bool b;
 PVOID pv;
 LONG lbound,ubound,sz;

 if(!IsEqualIID(riid,IID_NULL)) return DISP_E_UNKNOWNINTERFACE; // riid should 
                                                                // always be IID_NULL
 // Initialize the variants
 for(n=0;n<5;n++) VariantInit(&v[n]);
 // This event is not used in this code sample but is used for informative purposes
 if(dispIdMember==DISPID_BEFORENAVIGATE2) { // Handle the BeforeNavigate2 event  
  VariantChangeType(&v[0],&pDispParams->rgvarg[5],0,VT_BSTR);         // URL
  VariantChangeType(&v[1],&pDispParams->rgvarg[4],0,VT_I4);           // Flags
  VariantChangeType(&v[2],&pDispParams->rgvarg[3],0,VT_BSTR);         // TargetFrameName
  VariantChangeType(&v[3],&pDispParams->rgvarg[2],0,VT_UI1|VT_ARRAY); // PostData
  VariantChangeType(&v[4],&pDispParams->rgvarg[1],0,VT_BSTR);         // Headers
  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;
 }
 //This is where the event is captured, 
 //note I only need the second parameter which is the loaded URL
 if(dispIdMember==DISPID_DOCUMENTCOMPLETE){
  VariantChangeType(&v[0],&pDispParams->rgvarg[0],0,VT_BSTR); // URL
  OnDocumentComplete(NULL,(BSTR)v[0].bstrVal);
 }
 // Free the variants
 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:

C++
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);

 // This is the only way to make JavaScript work after the document is loaded
 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" );
 
 /* this is the code inserted on the page
 <div id="absoluteID123" style="float:right;padding: 20px;z-index:2000000">
  <table><tr><td>
   <div id="div123456"  style="z-index:2000002;width:307px;height:158px;
     background-image: url
     (http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);"/>
    <div id="divButton123" onclick="alert()" style="z-index:2000003;  
     position: relative; left: 279px;top: 6px; width:23px;height:24px;"/></div>
   </div>
 </td></tr></table></div>
*/

 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)