Programmer » Browser Helper Object

Browser Helper Object ("plug-in") for IE

This page has source code for a minimal C++ Browser Helper Object for Internet Explorer. It intercepts keyboard and mouse events, and changes the page's background color.

This code is heavily based on the code at http://www.adp-gmbh.ch/win/com/bho.html. What I've done is turn it into a complete project, one that compiles and builds cleanly, I've added COM registration stuff, and I updated bits of the code to use native interfaces (e.g. IHTMLEvents) instead of querying everything through IDispatch.


Source code

What follows is the "bho.cpp" source code file. You also need the "bho.def" module definition file, which is part of the download link above.

#include <windows.h>
#include <tchar.h>
#include <exdisp.h>
#include <exdispid.h>
#include <mshtml.h>
#include <mshtmdid.h>
#include <shlwapi.h>

HINSTANCE hInstance;
LONG gref=0;
const CLSID BhoCLSID = {0xC9C42510,0x9B41,0x42c1,0x9D,0xCD,0x72,0x82,0xA2,0xD0,0x7C,0x61};
#define BhoCLSIDs  _T("{C9C42510-9B41-42c1-9DCD-7282A2D07C61}")



class BHO : public IObjectWithSite, public IDispatch 
{ long ref;
  IWebBrowser2* webBrowser;
  IHTMLDocument* doc; IHTMLDocument2 *doc2;
  IHTMLWindow2 *win2;
public:
  // IUnknown...
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {if (riid==IID_IUnknown) *ppv=static_cast<BHO*>(this); else if (riid==IID_IObjectWithSite) *ppv=static_cast<IObjectWithSite*>(this); else if (riid==IID_IDispatch) *ppv=static_cast<IDispatch*>(this); else return E_NOINTERFACE; AddRef(); return S_OK;} 
  ULONG STDMETHODCALLTYPE AddRef() {InterlockedIncrement(&gref); return InterlockedIncrement(&ref);}
  ULONG STDMETHODCALLTYPE Release() {int tmp=InterlockedDecrement(&ref); if (tmp==0) delete this; InterlockedDecrement(&gref); return tmp;}

  // IDispatch...
  HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int FAR* pctinfo) {*pctinfo=1; return NOERROR;}
  HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR*  ppTInfo) {return NOERROR;}
  HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId) {return NOERROR;}
  
  HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr)
  { 
    // DISPID_DOCUMENTCOMPLETE: This is the earliest point we can obtain the "document" interface
    if (dispIdMember==DISPID_DOCUMENTCOMPLETE)
    { if (!webBrowser) return E_FAIL; 
      IDispatch *idisp; webBrowser->get_Document(&idisp);
      if (idisp && !doc) idisp->QueryInterface(IID_IHTMLDocument, (void**)&doc);
      if (idisp && !doc2) idisp->QueryInterface(IID_IHTMLDocument2, (void**)&doc2);
      if (doc2 && !win2) doc2->get_parentWindow(&win2);
      IConnectionPointContainer *cpc=0; if (doc) doc->QueryInterface(IID_IConnectionPointContainer, (void**) &cpc);
      IConnectionPoint* cp=0; if (cpc) cpc->FindConnectionPoint(DIID_HTMLDocumentEvents2, &cp);
      DWORD cookie; HRESULT hr; if (cp) hr=cp->Advise(static_cast<IDispatch*>(this), &cookie);
      if (cp) cp->Release(); if (cpc) cpc->Release(); if (idisp) idisp->Release();
      if (!doc || !doc2 || !win2 || hr!=S_OK) {release(); return E_FAIL;}
      return NOERROR;
    }

    if (dispIdMember==DISPID_HTMLDOCUMENTEVENTS_ONCLICK)
    { // This shows how to respond to simple events.
      MessageBox(0,_T("Try pressing some keys on the keyboard!"),_T("BHO"),MB_OK); 
      return NOERROR;
    }

    if (dispIdMember==DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN)
    { // This shows how to examine the "event object" of an event
      IDispatch *param1=0; if (pDispParams->cArgs==1 && (pDispParams->rgvarg)[0].vt==VT_DISPATCH) param1=(pDispParams->rgvarg)[0].pdispVal;
      IHTMLEventObj *pEvtObj=0; if (param1) param1->QueryInterface(IID_IHTMLEventObj, (void**)&pEvtObj);
      long keycode; HRESULT hr; if (pEvtObj) hr=pEvtObj->get_keyCode(&keycode);
      if (pEvtObj) pEvtObj->Release();
      if (!pEvtObj || hr!=S_OK) return E_FAIL;
      // This shows how to manipulate the CSS style of an element
      int i=keycode-32; if (i<0) i=0; if (i>63) i=63; i*=4;
      wchar_t buf[100]; wsprintfW(buf,L"rgb(%i,%i,%i)",i,255-i,i/2);
      IHTMLElement *body=0; doc2->get_body(&body);
      IHTMLStyle *style=0; if (body) body->get_style(&style);
      VARIANT v; v.vt=VT_BSTR; v.bstrVal=buf;
      if (style) style->put_backgroundColor(v);
      if (style) style->Release(); if (body) body->Release();
      if (!body || !style) return E_FAIL;
      return NOERROR;
    }
    return NOERROR;
  }

  // IObjectWithSite...
  HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void** ppvSite) {return E_NOINTERFACE;}
  HRESULT STDMETHODCALLTYPE SetSite(IUnknown* iunk)
  { // This is called by IE to plug us into the current web window
    release();
    iunk->QueryInterface(IID_IWebBrowser2, (void**)&webBrowser);
    IConnectionPointContainer *cpc=0; iunk->QueryInterface(IID_IConnectionPointContainer, (void**)&cpc);
    IConnectionPoint* cp=0; if (cpc) cpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &cp);
    DWORD cookie; HRESULT hr; if (cp) hr=cp->Advise(static_cast<IDispatch*>(this), &cookie);
    if (!webBrowser || !cpc || !cp || hr!=S_OK) {if (cp) cp->Release(); if (cpc) cpc->Release(); release(); return E_FAIL;}
    return S_OK;
  }

  // BHO...
  BHO() : ref(0), webBrowser(0), doc(0), doc2(0), win2(0) {};
  ~BHO() {release();}
  void release() {if (webBrowser) webBrowser->Release(); webBrowser=0; if (doc) doc->Release(); doc=0; if (doc2) doc2->Release(); doc2=0; if (win2) win2->Release(); win2=0;}

};







class MyClassFactory : public IClassFactory
{ long ref;
  public:
  // IUnknown... (nb. this class is instantiated statically, which is why Release() doesn't delete it.)
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) {if (riid==IID_IUnknown || riid==IID_IClassFactory) {*ppv=this; AddRef(); return S_OK;} else return E_NOINTERFACE;}
  ULONG STDMETHODCALLTYPE AddRef() {InterlockedIncrement(&gref); return InterlockedIncrement(&ref);}
  ULONG STDMETHODCALLTYPE Release() {int tmp = InterlockedDecrement(&ref); InterlockedDecrement(&gref); return tmp;}
  // IClassFactory...
  HRESULT STDMETHODCALLTYPE LockServer(BOOL b) {if (b) InterlockedIncrement(&gref); else InterlockedDecrement(&gref); return S_OK;}
  HRESULT STDMETHODCALLTYPE CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) {*ppvObj = NULL; if (pUnkOuter) return CLASS_E_NOAGGREGATION; BHO *bho=new BHO(); bho->AddRef(); HRESULT hr=bho->QueryInterface(riid, ppvObj); bho->Release(); return hr;}
  // MyClassFactory...
  MyClassFactory() : ref(0) {}
};


STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{ static MyClassFactory factory; *ppvOut = NULL;
  if (rclsid==BhoCLSID) {return factory.QueryInterface(riid,ppvOut);}
  else return CLASS_E_CLASSNOTAVAILABLE;
}

STDAPI DllCanUnloadNow(void)
{ return (gref>0)?S_FALSE:S_OK;
}

STDAPI DllRegisterServer(void)
{ TCHAR fn[MAX_PATH]; GetModuleFileName(hInstance,fn,MAX_PATH);
  SHSetValue(HKEY_CLASSES_ROOT,_T("CLSID\\")BhoCLSIDs,_T(""),REG_SZ,_T("BHO"),4*sizeof(TCHAR));
  SHSetValue(HKEY_CLASSES_ROOT,_T("CLSID\\")BhoCLSIDs _T("\\InProcServer32"),_T(""),REG_SZ,fn,((int)_tcslen(fn)+1)*sizeof(TCHAR));
  SHSetValue(HKEY_CLASSES_ROOT,_T("CLSID\\")BhoCLSIDs _T("\\InProcServer32"),_T("ThreadingModel"),REG_SZ,_T("Apartment"),10*sizeof(TCHAR));
  SHSetValue(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects\\")BhoCLSIDs,_T(""),REG_SZ,_T(""),sizeof(TCHAR));
  return S_OK;
}

STDAPI DllUnregisterServer()
{ SHDeleteKey(HKEY_CLASSES_ROOT,_T("CLSID\\") BhoCLSIDs);
  SHDeleteKey(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects\\")BhoCLSIDs);
  return S_OK;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{ if (fdwReason==DLL_PROCESS_ATTACH) hInstance=hinstDLL;
  return TRUE;
}

Note: I've only intercepted a few of the possible events. Others you might consider intercepting include DISPID_BEFORENAVIGATE DISPID_NAVIGATECOMPLETE DISPID_STATUSTEXTCHANGE DISPID_QUIT DISPID_DOWNLOADCOMPLETE DISPID_COMMANDSTATECHANGE DISPID_DOWNLOADBEGIN DISPID_NEWWINDOW DISPID_PROGRESSCHANGE DISPID_WINDOWMOVE DISPID_WINDOWRESIZE DISPID_WINDOWACTIVATE DISPID_PROPERTYCHANGE DISPID_TITLECHANGE  DISPID_FRAMEBEFORENAVIGATE DISPID_FRAMENAVIGATECOMPLETE DISPID_FRAMENEWWINDOW DISPID_BEFORENAVIGATE2 DISPID_NEWWINDOW2 DISPID_NAVIGATECOMPLETE2 DISPID_ONQUIT DISPID_ONVISIBLE DISPID_ONTOOLBAR DISPID_ONMENUBAR DISPID_ONSTATUSBAR DISPID_ONFULLSCREEN DISPID_ONTHEATERMODE DISPID_ONADDRESSBAR DISPID_RESETFIRSTBOOTMODE DISPID_RESETSAFEMODE DISPID_REFRESHOFFLINEDESKTOP DISPID_ADDFAVORITE DISPID_ADDCHANNEL DISPID_ADDDESKTOPCOMPONENT DISPID_ISSUBSCRIBED DISPID_SHELLUIHELPERLAST

Fantastic! This has been extremely useful. Thank you very much!
Hi. This site has extremely informative contents. Thank you for that.
 
I was looking at your BHO code. Do you know how to intercept/get all URLS that are in a website using the BHO? Could you let me know if you know it? My email is Nicholas.G5@gmail.com
 
Thanks.
Hi,
 
I have found a problem... I have rebuilt the project under vs2005, 32-bit, no Unicode, and everything works fine on my
 
win2003-32bit edition with IE6
winxp-sp2-32bit edition with IE7
 
but the BHO is not loaded on my win2003-64bit x64 system. I use the 32bit version of IE7. Everything looks good in the registry under
 
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects
 
and
 
HKEY_CLASSES_ROOT\Wow6432Node\CLSID
 
Additionally the BHO is visible in IE7 under Tools|Manage Ad-ons
 
Unfortunately the BHO cannot be activated and additional investigation with the use of SysInternals Process Explorer shows that bho.dll is not in the list of dlls shown for iexplore.exe. I have attempted to run the bho.dll under debugger (using C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE as the command) but none of the exports is reached.
 
Any ideas? I really like your compact code.
 
Continued from the previous comment - I can be reached at batkol@mindspring.com
Good! this article is highly appreciated...
Shakil Ahmad. PUCIT
Hello~
i'm now learning BHO,
if I want to collect the data witch users selected in the webpage,
how to realize it????
 
please help!!
 
raypp@qq.com
 
many thx ;D
Great stuff..
 
I'm trying to make the bho set a cookie when certain url is loaded.
 
if you happen to have any advice I'll be great full.
 
Thanks, Shai.
I am trying to download the latest version of Adobe Flash Player. I am getting this message:
Browser Helper Objects must be enabled to install this software. I am not finding BHOs in IE9.
Can anyone tell me how to enable them, if indeed they are there??
 
Thanks,
 
Judy
add comment  edit commentPlease add comments here