Advanced C# Programming: Developing IE Extensions

Browser extension software lives inside the browser, adding new features that make the browser suitable for specific personal or business needs. The most common business needs, which are easily accomplished with browser plugins are:

  • modify default search
  • add side frames
  • inject new content into existing webpage
  • communicate with remote servers or web services
  • search highlight
  • show ads
  • any other task that an intalled software program can do

In this article I will explain how to insert new static and dynamic content into anexisting webpage.

The Business Use Case: You want to develop an Internet Explorer extension that shows some fixed layer containing promotional offers and discounts for the goods you are selling.

To achieve that you need to develop a custom IE plugin using Browser Helper Objects (BHO) that will track user browsing activities and display your discounts based on some content keywords filtering.

So far so good. You know what should be done. Now let’s discover how it is done.

I will explain, step-by-step, how to accomplish this task as it is better to learn how to do it instead of using a ready-made solution since you don’t know how ready-made solutionswork and the moment you touch something to make it suit your needs it breaks down and you have no idea why. I will use .NET C# as a programming framework and language.

Step by Step Walk-through

1. Open your MS Visual Studio 2010.

2. Create a new project “Visual C$” -> “Windows” -> “Class Library” named “IEPlugin”

A new project is opened for you containing a single class “Class1”. Rename the class to “BHO”. You can use F2 on the CS file in the solution explorer.

Now you have to define the IObjectWithSite interface which the BHO class will implement. The IObjectWithSite interface provides simple objects with a lightweight siting mechanism (lighter than IOleObject). Often, an object must communicate directly with a container site that is managing the object. Outside of IOleObject::SetClientSite, there is no generic means through which an object becomes aware of its site. The IObjectWithSite interface provides a siting mechanism. This interface should only be used when IOleObject is not already in use. By using IObjectWithSite, a container can pass the IUnknown pointer of its site to the object through SetSite. Callers can also get the latest site passed to SetSite by using GetSite.

3. Add references and imports to the following components

using System.Runtime.InteropServices;
using SHDocVw;
using mshtml;

4. Define the IOleObjectWithSite interface just above your BHO class declaration/.

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

5. Now make the BHO class implement the IOleObjectWithSite interface.

    [
        ComVisible(true),
        Guid("1056BA25-DA81-56E3-A671B-D38A9B1B2142"),
        ClassInterface(ClassInterfaceType.None)
    ]
    public class BHO : IObjectWithSite
    {
        private WebBrowser webBrowser;

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

        public void OnDocumentComplete(object pDisp, ref object URL)
        {
            HTMLDocument document = (HTMLDocument)webBrowser.Document;


        }
    }

We have implemented the SetSite and GetSite methods and added an empty event handler for the document load event. Thus, we will receive an event every time the document is loaded into the browser.

6. Now we have to implement the OnDocumentComplete method to insert the Javascript code and the div element like this:

        public void OnDocumentComplete(object pDisp, ref object URL)
        {
            HTMLDocument document = (HTMLDocument)webBrowser.Document;

            IHTMLElement head = (IHTMLElement)((IHTMLElementCollection)document.all.tags("head")).item(null, 0);
            IHTMLScriptElement scriptObject = (IHTMLScriptElement)document.createElement("script");
            scriptObject.type = @"text/javascript";
            scriptObject.text = "function hidediv(){document.getElementById('myOwnUniqueId12345').style.visibility = 'hidden';}";
            ((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptObject);

            string div = "<div id="myOwnUniqueId12345" style="position:fixed;bottom:0px;right:0px;z-index:9999;width=300px;height=150px;">" +
                "<div style="position:relative;float:right;font-size:9px;"><a href="javascript:hidediv();">close</a></div>" +
                "My content goes here ...</div>";

            document.body.insertAdjacentHTML("afterBegin", div);
        }

First we inject the Javascript that we will use to close the div popup. Then we inject the HTML for the div element in the body with style that it is displayed in the bottom right corner of the browser. And that it is. You may want to filter our URLs and content for which you display the div. You can go like this:

        public void OnDocumentComplete(object pDisp, ref object URL)
        {
            if (URL.ToString().Contains("www.google.com"))
            {
                // Show div in here ...
            }
        }

7. Register your BHO to be loaded by the Internet Explorer like:

        public const string BHO_REGISTRY_KEY_NAME = "Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects";

        [ComRegisterFunction]
        public static void RegisterBHO(Type type)
        {
            RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHO_REGISTRY_KEY_NAME, true);

            if (registryKey == null)
                registryKey = Registry.LocalMachine.CreateSubKey(BHO_REGISTRY_KEY_NAME);

            string guid = type.GUID.ToString("B");
            RegistryKey ourKey = registryKey.OpenSubKey(guid);

            if (ourKey == null)
            {
                ourKey = registryKey.CreateSubKey(guid);
            }

            ourKey.SetValue("NoExplorer", 1, RegistryValueKind.DWord);

            registryKey.Close();
            ourKey.Close();
        }

        [ComUnregisterFunction]
        public static void UnregisterBHO(Type type)
        {
            RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHO_REGISTRY_KEY_NAME, true);
            string guid = type.GUID.ToString("B");

            if (registryKey != null)
                registryKey.DeleteSubKey(guid, false);
        }

The register method simply tells IE which is the GUID of your extension so that it can be loaded. The “No Explorer” value simply says that we don’t want to be loaded by Windows Explorer.

Now all you have to do is create a simple installation project that will install your IE plugin or install it manually.

That is all about it! Quick and simple.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read