IE Browser Helper Objects

I'm currently writing a new IE utility that requires a very tight relationship with the inner workings of Internet Explorer. Ostensibly, I needed to hook into the event model of the browser as it surfs the Web, moving from URL to URL. When I recognize the current URL as contained within a predefined collection, I want to take some action. While researching, I came across IE Browser Helper Objects and discovered how to implement them using .NET. After stumbling through a few ATL examples (which is nothing short of hell after looking at C# for a couple of years) I was pleasantly surprised at how easy it was to do in .NET.

A Browser Helper Object, BHO, is nothing more than a COM object loaded for each IE window. As a window is created, it creates its own copy of the BHO; and, when that window is closed, it destroys its copy of the BHO.

interface IObjectWithSite

To begin, A BHO must implement IObjectWithSite - a stupid name for a very tidy interface. The GUID must be exactly as shown here, otherwise it won't work.

using System;
using System.Runtime.InteropServices;
[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);
}

In general, you would never need to override the GetSite method. However, the SetSite method is invoked when the BHO is instantiated and when it is destroyed. In the former case, the site IWebBrowser2. In the latter case, the site argument is null .

Here's an example of how you might implement SetSite :

if (site == null)
{
  browser.DocumentComplete -=
   new DWebBrowserEvents2_DocumentCompleteEventHandler(
    this.OnDocumentComplete);
  browser = null;
}
else
{
  browser = (WebBrowser)site;
  browser.DocumentComplete +=
   new DWebBrowserEvents2_DocumentCompleteEventHandler(
    this.OnDocumentComplete);
}

The code above shows the two states of the site argument. If null , then unregister the event handler and dereference the browser instance. Otherwise, reference the browser instance and register an event handler. Obviously, you can register handlers for any events throws by the browser (and there are a ton!)

OnDocumentComplete is a private method with this signature:

private void OnDocumentComplete ( object frame, ref object urlObj);

While I haven't investigated the first argument, the second argument is an object from which you can extract the current URL using urlObj.ToString() .

Implementing IObjectWithSite

Remember that a BHO is a COM object, so we need to add attributes to the class definition. One attribute is a GUID that you will need to generate. Once you generate this GUID, you can use that value forever for this one class. Visual Studio .NET 2003 has a built-in utility found on the Tools menu to create a new GUID.

Here an example of implementing the BHO.

[ComVisible(true),
ClassInterface(ClassInterfaceType.None),
Guid("D5F20021-2084-4564-9449-BF195C577FDC")]
public class SiteWatcherBHO : IObjectWithSite
{
}

Auto-Register the COM object

To register this COM object, you can use the .NET regasm tool. This tool will interogate the attribute you applied to the class and register the DLL as a COM component in the System Registry. But this doesn't bind the DLL as a BHO. You first need to define a couple of special methods used only during COM registration and unregistration.

When you register the DLL using the command regasm /codebase, the regasm tool searches for a method with the ComRegisterFunction attribute and, if found, will execute it. Here is where you need to add custom code to set up the system registry.

When you register the DLL using the command regasm /unregister, the regasm tool searches for a method with the ComUnregisterFunction attribute and, if found, will execute it. Here is where you need to add custom code to delete the registry keys you created in the ComRegisterFunction method.

[ComRegisterFunction]
public static void RegisterBHO (Type t)
{
}

[ComUnregisterFunction]
public static void UnregisterBHO (Type t)
{
}

All the first method needs to do is add the registry key:

Software\Microsoft\Windows\CurrentVersion ...
    \Explorer\Browser Helper Objects

Then define a key below that, naming it the value of your GUID, for example:

Software\Microsoft\Windows\CurrentVersion ...
    \Explorer\Browser Helper Objects ...
        \{D5F20021-2084-4564-9449-BF195C577FDC}

The second method simply deletes this GUID key below the Browser Helper Objects key. You should not delete the Browser Helper Objects key because, most likely, there will be other third-party BHOs already defined there.

Debugging

Finally, you can debug the BHO code. But there is a catch. Since it's managed code and obviously IE is not managed code, it's rather difficult to jump in right at the first instantiation of the first BHO. But that's not a big deal.

I typically have my IE home page set to about:blank . That way I can start up the browser as fast as possible and go where I need to. So, start up the first IE window. The from VS.NET, use the Attach to Process item in the Debug menu to attach to iexplore.exe . Set breakpoints in your BHO. To break within the constructor, just open a second IE window.

You might also like...

Comments

Steven Cohn

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” - Brian Kernighan