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.
Comments