Click here to Skip to main content
15,886,689 members
Articles / Programming Languages / C++

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

Rate me:
Please Sign up or sign in to vote.
4.78/5 (9 votes)
18 Jul 2014CPOL3 min read 35.9K   1.3K   18   18
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)


Written By
Software Developer
United States United States
Born on 86, had my first computer at the age of 8, wrote my first code at the age of 15(pascal), began to study software engineering at 2005 and graduated in 2010. I have a dev blog www.vasilydev.blogspot.com. My real passion is 3D game programming and playing guitar. I've programmed stuff in C#, python, Delphi, PHP, C++, JS, QT and others...

Comments and Discussions

 
QuestionIE 11 Pin
Digi Pro25-Apr-18 15:55
Digi Pro25-Apr-18 15:55 
AnswerRe: IE 11 Pin
Vasily Tserekh10-Dec-18 5:27
Vasily Tserekh10-Dec-18 5:27 
QuestionShould not use ATL Pin
drslimw17-Aug-15 9:43
drslimw17-Aug-15 9:43 
AnswerRe: Should not use ATL Pin
Vasily Tserekh17-Aug-15 9:49
Vasily Tserekh17-Aug-15 9:49 
GeneralRe: Should not use ATL Pin
drslimw21-Aug-15 13:28
drslimw21-Aug-15 13:28 
GeneralRe: Should not use ATL Pin
Vasily Tserekh23-Aug-15 12:15
Vasily Tserekh23-Aug-15 12:15 
Remove ATL references and add this smart pointer implementation in plain c
http://www.codeguru.com/cpp/com-tech/complus/general/article.php/c11475/A-Pure-C-Implementation-of-a-COM-Smart-Pointer.htm[^]
QuestionRe: Should not use ATL Pin
louis.naber23-Jul-18 5:24
louis.naber23-Jul-18 5:24 
QuestionDoesn't add the html Pin
krisbcn3-Jul-15 2:55
krisbcn3-Jul-15 2:55 
AnswerRe: Doesn't add the html Pin
Vasily Tserekh8-Jul-15 2:53
Vasily Tserekh8-Jul-15 2:53 
GeneralRe: Doesn't add the html (solved) Pin
krisbcn8-Jul-15 8:44
krisbcn8-Jul-15 8:44 
QuestionTrying to watch code in debugger Pin
CalvinSays27-May-15 3:17
CalvinSays27-May-15 3:17 
AnswerRe: Trying to watch code in debugger Pin
CalvinSays28-May-15 12:13
CalvinSays28-May-15 12:13 
QuestionRe: Trying to watch code in debugger Pin
louis.naber23-Jul-18 5:43
louis.naber23-Jul-18 5:43 
Questionincompatible Pin
Member 854435726-Nov-14 3:39
Member 854435726-Nov-14 3:39 
AnswerRe: incompatible Pin
Vasily Tserekh26-Nov-14 4:20
Vasily Tserekh26-Nov-14 4:20 
GeneralRe: incompatible Pin
Member 854435726-Nov-14 5:16
Member 854435726-Nov-14 5:16 
GeneralMy vote of 3 Pin
Bruce B Baker21-Jul-14 23:26
professionalBruce B Baker21-Jul-14 23:26 
GeneralRe: My vote of 3 Pin
Vasily Tserekh22-Jul-14 6:36
Vasily Tserekh22-Jul-14 6:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.