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.



About the Author

Rosen Ivanov

Inacent: Offshore Software Development House offers custom software development services. Inacent is specialized in browser addons development. We have delivered hundreds of browser addons. Our company is the most influential provider when it comes to customizing browsers' behavior.

Related Articles

Comments

  • Javascript events does not work with external.menuArgument

    Posted by Arvind on 11/04/2014 06:00am

    Hi, I have tried to create toolbar button using BHO. All the events work fine. I can access toolbar button click function. Now I want to create button element inside that which gets created successfully but onclick event of that crated button does not work. Is there any special way to use javascript events with external.menuArgument? Even creating the sample code provided by you everything works but onclick event of anchor 'close' doesnt get called. I am not able to run any javascript event. Any help would be greatly appreciated. Thanks and regards, Arvind

    Reply
  • sdf

    Posted by we on 07/07/2013 03:33am

    sdf sdf

    Reply
  • In

    Posted by Kirill Shur on 10/04/2012 12:00pm

    [PreserveSig] int SetSite([In,MarshalAs(UnmanagedType.IUnknown)]object site);

    Reply
  • Question Regarding BHO

    Posted by surseh on 05/30/2012 04:19am

    Hi I studied all ur code.. i have small doubt.. can i change guids [ ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("Here I can Generate New Guid ") ] and another one is here.. [ ComVisible(true), Guid("Here I can Generate new Guid"), ClassInterface(ClassInterfaceType.None) ] Why these two Guid is created can u explain me plzzzzzzzzzzz

    Reply
  • student registration

    Posted by hail on 04/03/2012 04:06am

    likey

    Reply
  • A couple of questions

    Posted by IMOsiris on 07/13/2011 03:34pm

    I ran this code as shown (though I did have to change the second guid because it is was not in the right format). When I ran the .exe file, nothing appeared in my IE web browser. I opened and closed it, still nothing. Should I have seen something? And how would I add a custom toolbar button using this code?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds