Alright, I admit this page formatting looks a bit dorky, but there really isn’t much I can say because it was formatted using the latest brand new Office 2007. Unfortunately, Yahoo 360 doesn’t support the most excellent Windows Live Writer for blogging due to which such mishaps occur. Live Writer has the ability to download your blog’s default CSS and allow you to compose entries just the way you’d see them.
Anyways, the article below has been shortened due to Yahoo’s limits on my blog length. So I had to cut out some code samples that were included inline in the article, so please bear with any broken sentences or paragraphs that make no apparent sense in context. Blame yahoo for everything!
Anyways, the entire Visual Studio Solution used to write this article is provided here on my homepage: http://www.geocities.com/archisgore/projects/bin/SamplePlugin.zip
Just download it and hopefully you should be writing your plugins within a few hours. I’ve provided most of the skeleton code you’d need, so you won’t have to spend time on COM interoperability and stuff.
Motivation and Audience:
I was working with IE7 recently, and saw that the IE7 homepage is encouraging developers to contribute plug-ins for IE to make it cooler. I also found a bunch of people who wanted to write IE plugins but didn’t know how. Moreover, all information available on writing an IE plugin is available for COM-aware people. For someone who’s been a Java/C guy all his life, I realized that the documentation on writing an Internet Explorer plugin was very limited – and since there was quite a demand for a working skeleton code, in interests of self-popularization, I decided to write this (while also expecting a smartphone from Microsoft). Took me a week of sleepless nights to get it working, but once you know how to do it, it’s actually one of the simplest things to be done (if you don’t care about why and how it works – so long as it works, I’m happy). In fact, after this experience, I plan to write quite a bit of plugins for IE in the near future. The actual problem is only on getting the skeleton code running and getting it compiled and registered. Once this is done, the further functioning is really simple.
I’m writing this article keeping in mind IE7 running on Vista, so many steps regarding running stuff as “administrator” may not make sense to you unless you’re running the super-secure Vista.
Almost all of my code is stolen from various sources which I’ve listed at the end of this article so there’s no originality or any sort of breakthrough contribution of mine here. All I’ve done is merge together code from different people who’ve solved different parts of the overall problem and packaged it in one piece. The toolbar example is based on the famous BandObject example from www.codeproject.com. I suggest you first read that article before you read this one. This article is simply a mashup of that original article with modifications based on some code and replies on the talk-back forums.
However, at the end of the day, I feel that most of you want to simply get some skeleton code and begin contributing plugins to IE, so this article should ideally help you do that.
Plugin Basics:
Internet Explorer has plugins known as Browser Helper Objects. Actually, there are many more types of plugins like Custom Download Managers and stuff, but we’ll just focus on BHO’s since they’re what most people would be looking for. Besides, although many people differentiate between a toolbar and a BHO, I personally feel that a toolbar implemented as a BHO is easier to deal with, and anyways, most of the times, you’ll actually be implementing a BHO even though you call it “just a toolbar”.
Also, I shall show you how to write a toolbar and a BHO separately and get them to communicate while execution.
Browser Helper Object:
A Browser Helper Object is literally any .Net class that is visible to COM (registered for COM Interop) and that implements the IObjectWithSite interface (I know the name of the interface sounds geeky – like most other things in COM). I shall also demonstrate some tricks which need to be implemented to ensure our stuff works with the new tabs in IE7. Finally, I’ve included sample code at the end which should hopefully compile smoothly and work out of the box.
I recommend you go through the reference articles listed at the bottom of this page before you actually implement anything.
Creating the Project:
Assuming you’re using VS 2005, this is what you’d do to create your project.
Download the BandObject project contributed by Pavel Zolnikov which is cited below. It should come in handy when you attempt to create a toolbar. For a pure BHO, this shouldn’t matter much.
Create a standard “Class Library” project under any of the Managed Code languages (take your pick out of anyone of them like C#, VB.Net, etc.)
Add a class called SampleBHO (or whatever you prefer)
Above this class definition, but within the namespace, reference the IObjectWithSite interface. In short, just insert the code below:
[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);
}
And then make sure your class implements this interface. This can be done simply by first adding a ‘: IObjectWithSite’ in front of your class’s declaration but before it’s body. And then right-click on the interface name and you’ll get an option in the context-menu saying “Implement Interface”. Just click on the appropriate option to generate stubs for the interface’s methods.
Essentially, you should end up with a class that looks like this:
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("41DBDDF3-423B-4c61-93D5-1D19291CD655")]
[ProgIdAttribute("SamplePlugin.SampleBHO")]
public class SampleBHO : IObjectWithSite
{
Let’s walk through what each of these functions do:
The SetSite and GetSite functions were copied by me verbatim from other sample code, so I really am not the authority to explain what they do. Suffice it to say that after SetSite is executed, the webBrowser field of this class will be populated with a reference to the instance of the WebBrowser that this BHO is supposed to service.
The RegisterFunction and UnregisterFunction are also pretty standard things picked up from other articles. These functions simply register this class as a BHO at the right parts of the registry where IE will look for when it loads, and unregister this class when this assembly is uninstalled from the target machine, so that IE doesn’t keep loading it even if it doesn’t exist.
Creating the toolbar:
To create our toolbar, first add the BandObjectLib project to your current solution and reference it. A very important thing to remember is that a toolbar needs to be strong-name signed. This is particularly easy in VS 2005. Simply go to “Project->Properties” and you should find a “Signing” tab. On this tab, select the checkbox labeled “Sign the assembly”. And then either select “New” in the “Choose Strong Name file” option.
After this, create a class called “SampleToolbar” and inherit it from “ExplorerBarForm”
There’s one catch when adding references to this project. If you’ve referenced the “SHDocVw.dll” from the standard system directory, you’ll have to delete it. Instead when adding the reference, manually browse, and go to the “bin/Debug” folder of BandObjectLib and select the “Interop.SHDocVw.dll” file located there. This is necessary since SHDocVw.dll is a COM library which doesn’t come with it’s own Strong name signing. Hence, whenever VS finds such an assembly, it strong-name sign’s it’s interop wrapper that is generated. The catch is that if you reference it from two projects, viz. “BandObjectLib”, and “SamplePlugin” then you end up with two wrappers each with it’s different Strong-name signing. So you’ve gotta reference either one of the wrappers from both the projects to make sure your code will compile.
Here again, the class should now look something like this:
[Guid("0D34AA37-F614-424c-A996-CC0CB78D8143")]
[ClassInterface(ClassInterfaceType.None)]
[ProgIdAttribute("SamplePlugin.SampleToolbar")]
[BandObject("Sample Toolbar", BandObjectStyle.Horizontal | BandObjectStyle.ExplorerToolbar, HelpText = "Brings the internet experience to life!")]
public class SampleToolbar : ExplorerBarForm
{
If you notice, this class doesn’t have all the complex GetSite and SetSite stuff, since the BandObjectLib does that for us already.
Hopefully, with any luck, both the above classes should compile with relative ease (apart from some trouble you might have with adding the appropriate references, but I’ve given the project zipped along with this article to help you avoid that to some extent).
Getting colors right:
Now to make the background of the BandObject Transparent so that it sort of inherits IE’s default color scheme (though this isn’t the recommended way of doing things, it was the only way I could find out at such short notice). This one was again contributed by a forum member on the codeproject forums.
To do this, simply make the ExplorerBar object’s background color Transparent, and add the following code the ExplorerBar:
[DllImport("uxtheme", ExactSpelling = true)]
public extern static Int32 DrawThemeParentBackground(IntPtr hWnd, IntPtr hdc, ref Rectangle pRect);
protected override void OnPaint(PaintEventArgs e)
{
if (this.BackColor == Color.Transparent) {
IntPtr hdc = e.Graphics.GetHdc();
Rectangle rec = new Rectangle(e.ClipRectangle.Left, e.ClipRectangle.Top, e.ClipRectangle.Width, e.ClipRectangle.Height);
DrawThemeParentBackground(this.Handle, hdc, ref rec);
e.Graphics.ReleaseHdc(hdc);
} else base.OnPaint(e);
}
Though in my own implementation, I have commented the condition of transparency, so I draw my toolbar transparently regardless of what the setting is. This is done because I generally don’t care for a lot of extensibility, but you should be careful of it in case you want to make sure your users can change these settings once the code has been deployed.
Handling DOM events:
Finally, one quirk of handling events of the DOM objects within the document is that once you’ve registered handlers for them, they won’t respond to keyboard events or mouse clicks. The solution for this is very simple. Add the following code to your namespace (I prefer adding it in a separate file to remember where it came from).
// The delegate:
public delegate void DHTMLEvent(IHTMLEventObj e);
///
/// Generic Event handler for HTML DOM objects.
/// Handles a basic event object which receives an IHTMLEventObj which
/// applies to all document events raised.
///
[ComVisible(true)]
public class DHTMLEventHandler
{
public DHTMLEvent Handler;
HTMLDocument Document;
public DHTMLEventHandler(mshtml.HTMLDocument doc)
{
this.Document = doc;
}
[DispId(0)]
public void Call()
{
Handler(Docu
ment.parentWindow.@event);
}
}
And then instead of doing:
HTMLWindowEvents_Event temp = (HTMLWindowEvents_Event)document.parentWindow.onresize;
temp.onresize+=new HTMLWindowEvents_onresizeEventHandler(d_onresize);
You should add a handler like this:
DHTMLEventHandler Handler = new DHTMLEventHandler(doc);
Handler.Handler += new DHTMLEvent(DocumentWindow_onresize);
document.parentWindow.onresize = Handler;
And it should work just fine.
Connecting the Toolbar and BHO:
This is something that puzzled me quite a while and then I realized what an elegantly simple solution there was staring me in the face.
The IWebBrowser2 interface has GetProperty and PutProperty methods which allow you to submit objects to the WebBrowser’s PropertyBag alongwith a string tag to identify them.
When either, the toolbar or BHO’s SetSite method is called, we simply need to put the current instance ‘this’ of the respective object in the webbrowser’s PropertyBag.
Then all you have to do is to track the PropertyChanged event of the browser in both the Toolbar and BHO. Then you simply detect if that property was either that of the toolbar or the BHO. It’s obvious both objects won’t be loaded simultaneously. One object will follow the other. Whichever object got loaded first, can detect the second one’s PutProperty invocation through PropertyChanged, and can get the other’s reference.
Once we have the reference, we simply implement a setBHO method in the Toolbar and a setToolbar in the BHO. This way, the first object that got the reference, can pass on it’s own instance reference to the second object. We detect if the receiving object already had a reference to avoid a circular loop.
The property change events in the above code samples demonstrate how this is achieved.
Changes for IE7:
The following changes were applied by me to enable IE7’s tabs to be handled appropriately. If you notice, we capture an event webBrowser.WindowStateChanged which is fired every time the window state of the browser changes. This event is fired when a user toggles through tabs. We particularly care about this because a separate instance of your BHO will be loaded for each and every tab in IE7. Hence, if the tab which we’re supposed to service is hidden, we have to disable our services, lest they should interfere with completely irrelevant operations the user may be performing in another tab.
Other Minor changes:
I suggest you use the BandObjectLib implementation provided by me below since I’ve incorporated quite a few suggestions and recommendations based on mailing-list replies to certain problems in the original. They’re mostly minor tweaks that make the Toolbar movable and draggable across the Toolband in IE.
The dwModeFlags of the DESKBANDINFO structure in the original are incorrect, and based on some replies on the forums (I sincerely hate myself for having lost the link – sorry about not being able to give credit to the guy who figured this out), I’ve modified them and it’s working perfectly.
Also remember to use VS 2005 with SP1 for Vista, if you’re running on Vista, and while running VS, always run it as Administrator (if you’ve installed SP1, it will prompt you with this message every time you invoke VS).
Ending notes:
That ends my mashup of how to write a BHO/Toolbar for IE7 and handle all the quirks associated with it. Hopefully all your problems should have been solved with this article. As always, feedback, suggestions, and criticism will be appreciated.
I’m sorry for not being able to credit all the people whose contributions went into this article, but I hope that most of your common problems will not be solved through this one article instead of having to hunt on the net as I had to. And feel free to edit/modify the code provided.
Finally, you’ll need to register these assemblies that you create. For each DLL, you’ll need to call “regasm” from a command-prompt opened in Administrator mode, or ask VS to register it.
Also, for assemblies containing a Toolbar, they need to be registered in the Global Assembly Cache (hence the strong-name signing we had to do). For this, use gacutil, whose use is well documented on MSDN. The simplest invocation is ‘gacutil /if ’
Sometimes, you’ll need to close all instances of iexplorer.exe (IE) restart the “explorer.exe” shell process too in order to ensure that the DLL’s are not in use if you get errors while compiling or registering them.
Once this skeleton is done, you can deal with managed code most of the time, unless you need to interface with obscure parts of the platform. In general, all your managed UI objects from WinForms should work just fine with a BHO.
Read the following articles for reference:
1. Read the BHO articles:
a. http://www.15seconds.com/issue/040331.htm
c. http://weblogs.asp.net/stevencohn/articles/60948.aspx
2. This article shows the same thing through unmanaged code: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/cols/dnexpie/expie_hello_bho.asp
3. Read the BandObject article: http://www.codeproject.com/csharp/dotnetbandobjects.asp
4. If you face any problems while following this procedure, here’s an excellent place where you might find answers already, or would get an answer real fast: http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=924&SiteID=1
Thanks for your useful post. That’s all I need to study after suffing the Internet these days
Comment by vinhphu1711 — November 2, 2008 @ 10:41 am