Click here to Skip to main content
Click here to Skip to main content

Writing HTML content to a webpage's DOM with a C++ BHO

, 18 Jul 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
How to write your own HTML content into any webpage through BHOs in C++

Introduction

This article will cover all the 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 at 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 IE 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 recommed reading it in order to understand in detail this one. 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 capture 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.

// This is called by IE to notify us of events
// Full documentation about all the events supported by DWebBrowserEvents2 can be found at
//  <a href="http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx</a>
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:

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 to 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(<a href="http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif">http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif</a><a href="http://www.1-800-pcsupport.com/v.png);"/">);"/</a>>
    <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(<a href="http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif">http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif</a><a href="http://www.1-800-pcsupport.com/v.png);\"/">);\"/</a>>");
 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 worked in versions above IE 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 in which this one is based. I also made a nice installer using NSIS. If you need it just ask it on the comments.

History

This is the first version of the article

License

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

Share

About the Author

Vasily Tserekh
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 informatic engineering at 2005 and graduated in 2010, now I am currently working on a software development department in my company. Working with extjs mysql and php. I have a dev blog www.vasilydev.blogspot.com. My real pasion is 3D game programming and playing guitar. Ive programmed stuff in C#, python, Delphi, PHP, C++, JS, QT and others...
Follow on   LinkedIn

Comments and Discussions

 
Questionincompatible PinmemberMember 854435726-Nov-14 4:39 
AnswerRe: incompatible PinmemberVasily Tserekh26-Nov-14 5:20 
GeneralRe: incompatible PinmemberMember 854435726-Nov-14 6:16 
GeneralMy vote of 3 PinprofessionalBruce B Baker22-Jul-14 0:26 
GeneralRe: My vote of 3 PinmemberVasily Tserekh22-Jul-14 7:36 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150327.1 | Last Updated 18 Jul 2014
Article Copyright 2014 by Vasily Tserekh
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid