Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Subclassing Internet Explorer for Context Menu Overriding

0.00/5 (No votes)
1 Sep 2004 1  
Internet Explorer is a complex beast, I have managed to untangle the context menu to easily allow custom menus to appear when you right click

Introduction

This is my first article here at CodeProject so please excuse the format or anything that you are used to with the myriads of excellent posts on this site.

This article has been put together to fill a void that I found when trying to use the Internet Explorer object. I have recently undertaken a project at work that required me to embed a browser object into an application and display reports through Microsoft Reporting Services.

There was a need to stop the user from making use of the internet through the software, but also to stop them from looking at the underlying code as well as giving them extra options on the context menu.

Background

The Internet Explorer is an odd control. Essentially if you use Spy++ you can see that the object has children that protect the object from people picking off the handle to the real Internet Explorer easily. There was a need to iterate through the browser object looking for the child object called Internet Explorer_Server .

Much of the child search is done in the API Call 'EnumChildWindow'. The snippet of code makes use of some API calls to capture the correct handle for the Internet Explorer and then override the Context Menu and replace it with one that I wanted the user to see.

Win32 API in C# & Delegates

private delegate Int32 EnumChildProc(IntPtr hWnd, IntPtr lParam);
private delegate int Win32WndProc(IntPtr hWnd, int Msg, 
  int wParam, int lParam);

[DllImport("user32")] 
public static extern int GetClassName(int hwnd, 
  StringBuilder lpClassName, int nMaxCount);
[DllImport("user32")] 
public static extern int EnumChildWindows(int hwndParent, 
  Delegate lpEnumFunc, int lParam);
[DllImport("user32")] 
private static extern IntPtr SetWindowLong(IntPtr hWnd, 
  int nIndex, Win32WndProc newProc);
[DllImport("user32")] 
private static extern int CallWindowProc(IntPtr lpPrevWndFunc, 
  IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32")] 
private static extern IntPtr SetWindowLong(IntPtr hWnd, 
  int nIndex, IntPtr newProc);
[DllImport("user32")] 
public static extern int GetCursorPos(ref Point lpPoint);
[DllImport("user32")] 
public static extern int ScreenToClient(int hwnd, ref Point lpPoint);

To access these external API functions we need the namespace System.Runtime.InteropServices. This namespace provides a collection of classes useful for accessing COM objects, and native APIs. Shown below is the way the code is written in C#:

[DllImport("user32")] 
public static extern int EnumChildWindows(int hwndParent, 
  Delegate lpEnumFunc, int lParam);

The extern keyword indicates that the method is implemented externally. extern methods must be declared as static.

Within this API call you may notice the Delegate reference.

private delegate Int32 EnumChildProc(IntPtr hWnd, IntPtr lParam);

The delegate needs to be created in the same way that we create a class, shown below.

EnumChildProc myEnumChildProc = new EnumChildProc(EnumChild);
EnumChildWindows(hWnd.ToInt32(), myEnumChildProc, hWnd.ToInt32());

The first line shows the creation of the delegate, we pass to EnumChildProc a reference to the EnumChild function. We then use the EnumChildWindow API call to drill down into the child windows and compare the name of the window against the one that we are trying to find.

This CallBack feature will iterate through each child until it either runs out of children or finds the window.

Using the code

To make use of this code and get you on the road to creating your very own web aware app, you need to add the 'IEHwd.cs' class to your project.

Once this is done, you need to create an instance of the object at a global level to your form.

private IEHwd o = new IEHwd();

Once this is done you can now access the features within. You will need to pass to the class an instance of your form and also the context menu that you want to show when the right click is done.

I use the NavigateComplete2 event that gets fired once a response has been received from the web site that the page is being produced.

if(o.oldWndProc.ToInt32() == 0)
{
    IntPtr s = o.IEFromhWnd(wbBrowser.Handle);
                
    o.IEContextMenu = IECustomContext;
    o.CurrForm = this;
                
    o.StartSubclass(s);
}

The above statement goes into the IEHwd.cs class and retrieves the browser handle. We pass the web browser handle into the object as a starting point. We then pass the Context Menu into the class via a property and the same for the current form.

Once that is done we call StartSubclass where we store away the old handle so that we can exit the program cleanly at the end (when the form is discarded.)

switch(Msg)
{
    case WM_RBUTTONDOWN:
    {
        //Right Mouse Button has been pressed

        //We need to capture the area on the screen

        //Next to the mouse we post our Custom Context Menu

        Point p = new Point();
        GetCursorPos(ref p);
        ScreenToClient(currForm.Handle.ToInt32(), ref p);
        ieContextMenu.Show(currForm, p);

        return 0;
    }
    case WM_RBUTTONDBLCLK:
        return 0;
    default:
        break;
}

When the user clicks the right mouse button over the Internet Explorer window we want to capture the right click event. This happens by checking for the WM_RBUTTONDOWN. Once pressed we need to get the point where the mouse is using the the GetCursorPos. Then we make use of the ScreenToClient API call to make sure that the Context Menu appears under the mouse and not somewhere else on the screen.

Finally we show the Context Menu that we passed into the class earlier and return 0 to stop the real Internet Explorer Context Menu from showing.

You may notice that I also trap the WM_RBUTTONDBLCLK message also. I found if you repeatedly clicked the right mouse button it would display the real Internet Explorer Context Menu.

Points of Interest

I leant that there are some interesting features available in the API. I have not used the API as much as I should and will endeavor to make more use of it in the future.

Credits

I would like to acknowledge Chris Wilcock for assistance with the API calls.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here