How to create Internet Explorer addin in C#

There is an less known possibility to easily build Addins to IE in C#. Lets look at the simple sample.

Few starting words

There are few types of addins you can build in IE, lets concentrate on the addins that would help you manipulate with rendered page. These addins are called Browser Helper Objects. They are intended to be developer as COM components, but that’s nothing we cannot handle in C# prety easy.

What we need to do

First, we would like to create some code to register our addin in Internet Explorer addins Registry hive (HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects). Because we are going to create a managed COM library, we can take advantage of ComRegisterFunction attribute. method with such a decoration will be called during COM registration process.

Sample registration and unregistration methods:

public static string BHOKEYNAME =
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects";
[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
  RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);
  if (registryKey == null)
    registryKey = Registry.LocalMachine.CreateSubKey(BHOKEYNAME);
  string guid = type.GUID.ToString("B");
  RegistryKey ourKey = registryKey.OpenSubKey(guid);
  if (ourKey == null)
    ourKey = registryKey.CreateSubKey(guid);
  ourKey.SetValue("Alright", 1);
  ourKey.SetValue("NoExplorer", 1);
  registryKey.Close();
  ourKey.Close();
}

[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
  RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);
string guid = type.GUID.ToString("B");
if (registryKey != null)
registryKey.DeleteSubKey(guid, false);
}

Browser Helper Objects basics

Each BHO has to implement COM interface IObjectWithSite, so we need to create such a interface in our project.

IObjectWithSite interface sample:

[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);
}

The interface has two main methods. SetSite is called when BHO is being created by IE and its passing an instance of WebBrowser object, which we can use to manipulate with dom, or catch various DOM events as we use later. GetSite in the other hand is being called by IE to get the current instance of its WebBrowser.

Here is sample implementation of SetSite and GetSite in a class that inherits from our IObjectWithSite:

public int SetSite(object site)
{
  if (site != null)
  {
    webBrowser = (SHDocVw.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;
}

Working with the DOM

In previous code, we have set up our WebBrowser instance which we are going to work with. We have also setup two DOM events, that we are going to handle. DocumentComplete is being called after document successfully is rendered. (But there might be still some scripts, or AJAX requests on the page running). Second event, BeforeNavigate is being called after you click ENTER when you put a new address in the IE address bar.

public void OnDocumentComplete(object pDisp, ref object URL)
{
  document = (HTMLDocument)webBrowser.Document;
  foreach (IHTMLInputElement tempElement in document.getElementsByTagName("INPUT"))
  {
    if (string.IsNullOrEmpty(tempElement.type))
      continue;

    if (tempElement.type.ToLower() == "text" || tempElement.type.ToLower() == "password")
    {
      IHTMLElement elem = (IHTMLElement)tempElement;
      elem.style.border = "1px solid red";
    }
  }
}

In the code above, we have handler the event DocumentComplete. Using our previously obtained WebBrowser object (in SetSite method), we can traverse the DOM of current page and do some funny stuff with it. In this case, I am setting a red border to all text or password input boxes in the page.

Registering the component

Because we are working on a COM component, we need to register it manually. to do this, I use following command:

regasm /codebase BHOHelloWorld.dll

and to unregister just change the parameter to:

regasm /u BHOHelloWorld.dll

After registration, you have to enable the addin in Internet Explorer Addin Manager.

Here is how www.microsoft.com looks with our BHO (notify the ugly red border around the textbox):

Download the sample here

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s