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






4.73/5 (21 votes)
This article demonstrates how to modify pages loaded into the Internet Explorer browser in order to show your own information related to the current page. For example: some ads, or special offers, or anything else that you may need.
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.