Microsoft has provided the Browser Helper Object (BHO) to let developers "drive" Internet Explorer. The first BHO was introduced in 1997 with IE 4.0. I have been writing programs on BHO for months. It could be quite depressing at the very beginning to learn all those things. I am writing this article to help beginners like me get familiar with BHO as soon as possible.
My personal interest is actually C++. C++ programs can be a lot less memory-consuming than C# programs. But C# does provide better service on BHO compared to C++. My first BHO program was written in C++. It took me quite a while to figure out how to handle BHO under VC++. But in C#, it only took me a few minutes. Also, C# has a lot of pleasant features such as foreach
and type conversion.
To set up a BHO Hello World project, let us first start a C# Class Library, as BHO is written in a .dll attached to IE. You don't need Visual Studio 2005; C# Express is totally enough.
After we have this empty project, let's add a folder named BHO and an empty .cs file into the folder.
The first file has to been named IObjectWithSite
to notify that this is a BHO project. To know more about this interface, please refer to http://msdn2.microsoft.com/en-us/library/Aa768220.aspx.
We also need to add two functions:
GetSite
: Gets the last site set with IObjectWithSite::SetSite
. If there is no known site, the object returns a failure code.SetSite
: Provides the site's IUnknown
pointer to the object.public interface IObjectWithSite { [PreserveSig] int SetSite([MarshalAs(UnmanagedType.IUnknown)]object site); [PreserveSig] int GetSite(ref Guid guid, out IntPtr ppvSite); }
Don't forget to add:
using System.Runtime.InteropServices;
Add another .cs file named BHO.cs.
Add a class called BHO
in the newly added file (BHO.cs). The class contains the interface IObjectWithSite
.
class BHO:IObjectWithSite
To use BHO, we need to have two references: SHDocVw.dll and MSHTML.dll. You can find them in the Windows\System32 folder. SHDocVw is a Microsoft Shell Doc Object and Control Library. MSHTML: All interfaces for accessing the Dynamic HTML (DHTML) Object Model are based on IDispatch
and are the basis of access to the object model that is also used by scripts. http://msdn2.microsoft.com/en-us/library/bb498651.aspx.
using SHDocVw; using mshtml; using System.Runtime.InteropServices; namespace BHO_HelloWorld { class BHO:IObjectWithSite {} }
Having using SHDocVw
is not enough, you need to add references to the project. Add SHDocVw. Later we are going to use MessageBox
, so we also need to add a Windows Forms reference.
Now in BHO.cs: we add two variables into the class, WebBrowser
and HTMLDocument
. From their names, you can easily figure out what they do.
Besides the two methods we defined in the IObjectWithSite
interface, we also need to add OnDocumentComplete
. You can name the function anything you want as long as the parameters are the same as what's defined in the CDHtmlDialog
class. Later you need to carefully attach it to an Event Handler. For consistency of code, we name it OnDocumentComplete
. OnDocumentComplete
is a function of the CDHtmlDialog
class (http://msdn2.microsoft.com/en-us/library/8bed8k60(VS.80).aspx. It will be triggered if the HTMLDocument
downloading is complete, in other words, when your page is loaded. You can also use Navigate()
or OnBeforeNavigate()
. Please refer to http://msdn2.microsoft.com/en-us/library/8k5z3ekh(VS.80).aspx to find out what you need exactly.
class BHO: IObjectWithSite { WebBrowser webBrowser; HTMLDocument document; publiv void OnDocumentComplete(object pDisp, ref object URL) { } public void SetSite(object site) { } public int GetSite(ref Guid guid, out IntPtr ppvSite) { } }
Under IObjectWithSite.cs: you need to point out the GUID of IE for the program, so it can attach to IE.
[ 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); }
Also, you need to assign a GUID for your own program in BHO.cs. You can use the System.Guid.NewGuid()
method to get one, which is really neat compared to C++.
[ ComVisible(true), Guid("8a194578-81ea-4850-9911-13ba2d71efbd"), ClassInterface(ClassInterfaceType.None) ]
You cannot just leave SetSite
and GetSite
blank. fill them in. This step is to tell IE that the DocumentCompletent
event is attached to OnDocumentComplete
in our program.
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; }
Add one more reference:
using Microsoft.Win32;
In BHO.cs, we need to write two functions for register/unregister of this DLL.
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); 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); }
Now compile; under your Release folder, you will find the .dll of your project.
Then, use regasm /codebase "BHO HelloWorld.dll" to register the DLL. We have a problem here. REGASM will tell us me it's not registered. Why?
Because we didn't set the BHO
class as public
. That's why.
public class BHO:IObjectWiteSite
Now, do it again. It'll be successful.
Open your Registry. Find out the Browser Helper Object under LOCAL_MACHINE->SOFTWARE->MICROSOFT->WINDOWS->EXPLORER.
Now the program is officially attached to your BHO. We need to fill in the OnDocumentComplete
function. It's really neat to use C#'s foreach
loop rather than the for
loop in C++. Because you don't need to care about the indexer. We will also see the type conversion is quite easy. This is an example of how find out the NAME
attribute of an IHTMLInputElement
.
An IHTMLInputElement
is an Input element in an HTML page.
If the IHTMLInputElement
does not have a name
attribute, we will fetch the ID
attribute. Then pop up the value.
public void OnDocumentComplete(object pDisp, ref object URL) { document = (HTMLDocument)webBrowser.Document; foreach ( IHTMLInputElement tempElement in document.getElementsByTagName("INPUT")) { System.Windows.Forms.MessageBox.Show( tempElement.name!=null?tempElement.name:"it sucks, no name, try id"+ ((IHTMLElement)tempElement).id ); } }
There you go, see?
Now, let's try using BeforeNavigate2()
.
As we can see, there is BeforeNavigate
and BeforeNavigate2()
. We go for the latter one. If you are interested, you can use the first one.
Add the function prototype:
public void OnBeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
Set up the hook:
public int SetSite(object site) { if (site != null) { webBrowser = (WebBrowser)site; webBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete); webBrowser.BeforeNavigate2+= new DWebBrowserEvents2_BeforeNavigate2EventHandler(this.OnBeforeNavigate2); } else { webBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete); webBrowser.BeforeNavigate2 -= new DWebBrowserEvents2_BeforeNavigate2EventHandler(this.OnBeforeNavigate2); webBrowser = null; } return 0; }
Now we have to steal the password on an Input Password element:
public void OnBeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel) { document = (HTMLDocument)webBrowser.Document; foreach(IHTMLInputElement tempElement in document.getElementsByTagName("INPUT")) { if(tempElement.type.ToLower()=="password") { System.Windows.Forms.MessageBox.Show(tempElement.value); } } }
See how easy you can get it.
In conclusion, it is really easy to handle BHO with C#. That is why there are so many IE add-ons. I hope this was useful to you. To save your time, you can use the project template I made. Download it and put it under your Visual Studio 2005\Templates\ProjectTemplates folder (it is usually under My Documents).
Reference URL:http://www.codeproject.com/Articles/19971/How-to-attach-to-Browser-Helper-Object-BHO-with-C