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

Inject HTML and JavaScript into an existing page with BHO using MS Visual Studio 2010 and C#

By , 22 Jan 2011
Rate this:
Please Sign up or sign in to vote.

Introduction

A browser extension software lives inside the browser and adds new features making the browser suitable for specific personal or business needs. The most common business needs that are easily accomplished with browser plug-ins are: modify default search, add side frames, inject new content into existing webpage, communicate with remote servers or Web Services, search highlight, show ads, and any other task that an installed software program can do. In this article, I will explain how to inject new static and dynamic content into an existing webpage.

Background

Browser extensions allow developers to provide easy access to their browser enhancements by adding elements (like an Explorer Bar) to the default user interface. Introduced in Microsoft Internet Explorer 4.0, this feature enables developers to create Explorer Bars and add entries into the standard context menus. Beginning with Internet Explorer 5, this feature allows developers to add entries into the Tools menu and buttons to the toolbar.

A Browser Helper Object (BHO) runs within Internet Explorer and offers additional services, often without any obvious user interface. For example, a BHO might highlight terms of interest to the user, such as addresses.

For more background information, see: http://msdn.microsoft.com/en-us/library/ms976373.aspx.

The Business Use Case

You want to develop an Internet Explorer extension which shows some fixed layer containing promotional offers and discounts for the goods you are selling. To achieve that, you need to develop a custom IE plug-in using BHO that will track user browsing activities and display your discounts based on some content keywords filtering.

So far so good. You know what should be done. Now let's discover how it is done.

Step by Step Walk-Through

I will explain here a step by step guide on how to accomplish this task as it is better to learn how to do it instead of just take a ready solution which you don't know how it works, and the moment you touch something to make it suit your needs, it breaks down and you have no idea why. I will use .NET C# as a programming framework and language.

1. Open MS Visual Studio 2010 and create a new project "Visual C#" -> "Windows" -> "Class Library" named "IEPlugin"

A new project is opened for you containing a single class Class1. Rename the class to BHO. You can use F2 on the CS file in the Solution Explorer.

From this high-level overview of Browser Helper Objects, one concept emerges clearly: a BHO is a dynamic-link library (DLL) capable of attaching itself to any new instance of Internet Explorer and, under certain circumstances, also Windows Explorer. Such a module can get in touch with the browser through the container's site. In order to do that, we must implement the IObjectWithSite interface.

2. Implement the IObjectWithSite interface

Now you have to define the IObjectWithSite interface which the BHO class will implement. The IObjectWithSite interface provides simple objects with a lightweight siting mechanism (lighter than IOleObject). Often, an object must communicate directly with a container site that is managing the object. Outside of IOleObject::SetClientSite, there is no generic means through which an object becomes aware of its site. The IObjectWithSite interface provides a siting mechanism. This interface should only be used when IOleObject is not already in use. By using IObjectWithSite, a container can pass the IUnknown pointer of its site to the object through SetSite. Callers can also get the latest site passed to SetSite by using GetSite.

Add references and imports to the following components:

using SHDocVw;
using mshtml;
using System.IO;
using Microsoft.Win32;
using System.Runtime.InteropServices; 

Define the IOleObjectWithSite interface just above your BHO class declaration like this:

[
    ComVisible(true),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")
]
public interface IObjectWithSite
{
    [PreserveSig]
    int SetSite([MarshalAs(UnmanagedType.IUnknown)]object site);
    [PreserveSig]
    int GetSite(ref Guid guid, out IntPtr ppvSite);
}

Make sure you don't change the GUID!

3. Make the BHO class implement the IOleObjectWithSite interface

The SetSite() method is where the BHO is initialized and where you would perform all the tasks that happen only once. When you navigate to a URL with Internet Explorer, you should wait for a couple of events to make sure the required document has been completely downloaded and then initialized. Only at this point can you safely access its content through the exposed object model, if any. This means you need to acquire a couple of pointers. The first one is the pointer to IWebBrowser2, the interface that renders the WebBrowser object. The second pointer relates to events. This module must register as an event listener with the browser in order to receive the notification of downloads and document-specific events.

[
        ComVisible(true),
        Guid("2159CB25-EF9A-54C1-B43C-E30D1A4A8277"),
        ClassInterface(ClassInterfaceType.None)
]

public class BHO : IObjectWithSite
{
  private WebBrowser webBrowser;

  public int SetSite(object site)
  {
    if (site != null)
    {
        webBrowser = (WebBrowser)site;
        webBrowser.DocumentComplete += 
          new DWebBrowserEvents2_DocumentCompleteEventHandler(
          this.OnDocumentComplete);
    }
    else
    {
        webBrowser.DocumentComplete -= 
          new DWebBrowserEvents2_DocumentCompleteEventHandler(
          this.OnDocumentComplete);
        webBrowser = null;
    }

    return 0;

  }

  public int GetSite(ref Guid guid, out IntPtr ppvSite)
  {
    IntPtr punk = Marshal.GetIUnknownForObject(webBrowser);
    int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
    Marshal.Release(punk);
    return hr;
  }

  public void OnDocumentComplete(object pDisp, ref object URL)
  {
    HTMLDocument document = (HTMLDocument)webBrowser.Document;
  }

}

The BHO site is the COM interface used to establish a communication. Define a GUID attribute for your BHO as it will be used later on during registration. We have implemented the SetSite and GetSite methods and added an empty event handler for the document loaded event. Thus, we will receive an event every time the document is loaded into the browser.

4. Implement the OnDocumentComplete method to inject the JavaScript code and the div element

public void OnDocumentComplete(object pDisp, ref object URL)
{
    HTMLDocument document = (HTMLDocument)webBrowser.Document;

    IHTMLElement head = (IHTMLElement)((IHTMLElementCollection)
                           document.all.tags("head")).item(null, 0);
    IHTMLScriptElement scriptObject = 
      (IHTMLScriptElement)document.createElement("script");
    scriptObject.type = @"text/javascript";
    scriptObject.text = "\nfunction hidediv(){document.getElementById" + 
                        "('myOwnUniqueId12345').style.visibility = 'hidden';}\n\n";
    ((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);

    string div = "<div id=\"myOwnUniqueId12345\" style=\"position:" + 
                 "fixed;bottom:0px;right:0px;z-index:9999;width=300px;" + 
                 "height=150px;\"> <div style=\"position:relative;" + 
                 "float:right;font-size:9px;\"><a " + 
                 "href=\"javascript:hidediv();\">close</a></div>" +
        "My content goes here ...</div>";

    document.body.insertAdjacentHTML("afterBegin", div);
}

First we inject the JavaScript that we will use to close the div popup. Then we inject the HTML for the div element in the body with style that is displayed in the bottom right corner of the browser. And that is it. You may want to filter our URLs and content for which you display the div. You can go like this:

if (URL.ToString().Contains("www.google.com")) 
{
    // Show div only in here ...
} 

5. Register your BHO to be loaded by Internet Explorer

public const string BHO_REGISTRY_KEY_NAME = 
   "Software\\Microsoft\\Windows\\" + 
   "CurrentVersion\\Explorer\\Browser Helper Objects";

[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
    RegistryKey registryKey = 
      Registry.LocalMachine.OpenSubKey(BHO_REGISTRY_KEY_NAME, true);

    if (registryKey == null)
        registryKey = Registry.LocalMachine.CreateSubKey(
                                BHO_REGISTRY_KEY_NAME);

    string guid = type.GUID.ToString("B");
    RegistryKey ourKey = registryKey.OpenSubKey(guid);

    if (ourKey == null)
    {
        ourKey = registryKey.CreateSubKey(guid);
    }

    ourKey.SetValue("NoExplorer", 1, RegistryValueKind.DWord);

    registryKey.Close();
    ourKey.Close();
}

[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
    RegistryKey registryKey = 
      Registry.LocalMachine.OpenSubKey(BHO_REGISTRY_KEY_NAME, true);
    string guid = type.GUID.ToString("B");

    if (registryKey != null)
        registryKey.DeleteSubKey(guid, false);
}

The Register method simply tells IE which is the GUID of your extension so that it could be loaded. The "No Explorer" value simply says that we don't want to be loaded by Windows Explorer.

Now all you have to do is create a simple installation project that will install your IE plug-in or install it manually.

That is all about it! Quick and simple.

License

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

About the Author

inacent.com
CEO Inacent
Bulgaria Bulgaria
Inacent: Outsourcing Professional Software Development
 
Inacent is a software company specialized in outsourcing custom software development, browser plugin development, web design and search engine optimization.

Comments and Discussions

 
QuestionDoes BHO supports all browsers? or is limited to IE? PinmemberMember 820324416-Mar-13 21:53 
QuestionThanks for the guidance..but neither Javascript nor Div are getting inserted Pinmemberjaggudadda18-Jan-13 18:51 
QuestionPost source PinmemberTheFoyer17-Oct-12 18:36 
Questionregasm problem Pinmembersuper sheep25-Jun-12 17:55 
AnswerRe: regasm problem PinmemberMember 1042313924-Nov-13 11:51 
QuestionExcellent Article Pinmemberxzz01951-Jun-12 17:20 
Questiongreat article PinmemberIamHanson17-Oct-11 21:05 
Questioninstallation and debug mode [modified] Pinmemberrvleonce23-Aug-11 22:37 
QuestionGreat tutorial...have 1 question PinmemberTJazz22-Mar-11 3:28 
QuestionCongratulations and installation questions Pinmemberjzamudio15-Feb-11 22:08 
AnswerRe: Congratulations and installation questions Pinmemberjzamudio17-Feb-11 20:56 
GeneralMy vote of 5 Pinmembersean133226-Jan-11 2:56 
GeneralMy vote of 5 PinmemberYusuf25-Jan-11 8:29 
GeneralMy vote of 5 Pinmemberwowosb23-Jan-11 14:25 
GeneralMy vote of 5 Pinmembervalery possoz22-Jan-11 12:23 
QuestionNo code yet? PinmemberManfred R. Bihy22-Jan-11 8:15 
AnswerRe: No code yet? Pinmemberwaisol22-Jan-11 8:33 
GeneralMy vote of 5 PinmemberManfred R. Bihy22-Jan-11 8:09 

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 | Mobile
Web04 | 2.8.140421.2 | Last Updated 22 Jan 2011
Article Copyright 2011 by inacent.com
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid