Export (0) Print
Expand All
38 out of 51 rated this helpful - Rate this topic

Creating Add-ons for Internet Explorer: Customizing Menus

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see IE Developer Center.

Kent Sharkey
July 2007

Contents

Creating Menu Items with JavaScript
Creating DLL-based Menu Items
Conclusion

Summary: Menu items are at the core of most Internet Explorer add-ons, whether it's an addition to the Tools menu or the context menu for a particular piece of content in a Web page. Creating these menu items is relatively easy, but the options available are numerous. This article looks at those options and discusses how to create stand-alone menu add-ons, as well as how to integrate the menus into larger add-ons.

Add-ons to Internet Explorer 7 take many forms, but many include a menu item. The menu may actually be the entire add-on, enabling a user to rapidly send some selected text to a search engine, for example. Alternatively, the menu item may be part of a larger add-on, either initiating the add-on or controlling it. Therefore, knowing how to create menu items is an essential part of add-on development.

Creating menu items for Internet Explorer involves an interplay of your code, Internet Explorer, and the registry. This article looks at the three types of code that may be used to power a menu item: executables, script, and COM components.

Not all menus are accessible by your add-ons, however. Add-ons are limited to the Tools and Help menus, and to the various context menus used on a Web page. Personally, I think this is a good thing. Imagine the havoc a hacker might create if they could add items to the File menu. The user might think they are printing a Web page, when in fact they might be running malicious code inserted as an add-on.

Creating Menu Items with JavaScript

The simplest means of creating menu items is with JavaScript. You can create a target page which is primarily a script block containing JavaScript. Once this page is registered as an add-on, users can execute it from within Internet Explorer. This enables rapid prototyping and iteration of your script.

Adding to the Top-Level Menu

You create new items for the Tools menu by adding entries to the registry. Each new Tools menu item is added under the registry key HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Extensions. You can install context menu extensions for the current user only.

Each menu item is identified by a GUID. Therefore, the first step in creating a new menu item is the creation of a GUID. Use Create GUID on the Tools menu in Visual Studio 2005 to generate the new value. This tool is distributed with Visual Studio, the Windows SDK and elsewhere. The tool is called GuidGen.exe.

Once you have created the new entry using the GUID, there are a number of values you can add to change the behavior of your menu entry. Each of these values is a string (REG_SZ).

EntryOptional?Description
CLSIDRequiredThis entry identifies the extension as a Tools menu entry. It must have the value {1FBA04EE-3024-11D2-8F1F-0000F87ABD16} to identify it appropriately to the system as a menu entry.
MenuTextRequiredThis provides the text used for your new menu item. As with any menu items, you can include the ampersand (&) character to identify the access key for the new menu item. However, there is no way to guarantee this value is unique.
MenuStatusBarOptionalProvides the text that will appear in the status bar (if visible) when your menu item is highlighted.
MenuCustomizeOptionalSets the new menu item to appear in the Help menu if the value of this entry is set to help. You may want to add a menu item to the Help menu to provide a source of help for your add-on.
ClsidExtensionOptionalUsed if the code for the menu item is provided by a COM object (see Creating DLL-based Menu Items below for details).
ScriptOptionalUsed if the code for the menu item is provided by a block of JavaScript.
ExecOptionalUsed if the menu item will call an external executable. This provides handy access to an external application from Internet Explorer.

Note: ClsidExtension, Script, and Exec are mutually exclusive. Choose only one of these options.

Note: As with any changes to the registry, you should back up the settings before making any changes. In addition, you should be comfortable with making the changes and have the ability to restore the registry.

The entries in the following table create a new add-on for the current user. This add-on starts the Windows Media Player executable. They are all defined for a new registry key under HKCU\Software\Microsoft\Internet Explorer\Extensions. This key was named using the GUID {0B1889FA-17A8-4848-BF39-19DDCCEE42FB} (the actual GUID here is not important, as long as it is unique on your system).

EntryValue
CLSID{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}
MenuTextStart Media & Player
MenuStatusBarPlay digital media including music, videos, CDs, and DVDs.
ExecC:\Program Files\Windows Media Player\wmplayer.exe

The next time Internet Explorer is started, you should see the new menu item under the tools menu (see Figure 1).

Bb735853.IEAddOnsMenus_Fig01(en-us,VS.85).gif

Figure 1: Adding a menu item to the Tools menu

Adding to a context menu

Just as with creating new entries in the Tools menu, changing the context menu requires entries in the registry, but in a different location. To add a new entry to the context menu for Internet Explorer, add a new item under the key HKCU\Software\Microsoft\Internet Explorer\MenuExt. Context menu items support two additional values, Contexts and Flags.

The Contexts value identifies the content on the Web page that will be affected by the new menu item. If you think about a Web page, it generally has a variety of content: text, images, anchors, tables, etc. Each of these has its own context menu. The Contexts value is a DWORD entry that identifies which of these will use your new menu item.

ContextValue (hex)Value (base 10)Description
Default0x0 or 0x10 or 1This is the context menu that will be used when none of the other contexts are in use. You would use this context typically when your add-on needs access to the entire Web page. For example, if you wanted to export the page to another application, extract all the images, or count the number of links on the page, you'd include the default context.
Images0x22This is the context menu that will be used when the user right-clicks an image. This menu typically has elements that enable you to save or edit the image. You may want to add a menu item to edit using a specific application or to send via another means (perhaps via instant messenger).
Controls0x44Used for the context menu for form controls. Typically this includes options for editing the text in these controls, but you may provide menu items to auto-populate the fields.
Tables0x88Used for the context menu for tables and table cells. This is arguably the least-frequently used value included in the Contexts item. Typically, it is used only for pages that enable the user to edit the page, enabling the addition of new rows and columns to a table.
Text selection0x1016This identifies the context menu that is applied when text is selected in the browser. This can be a powerful tool when used in add-ons. You could pass the selection on to another page or application, or extract the source of just the selection rather than the entire page.
Anchor0x2032Used for the context menu applied when the user right-clicks an anchor tag. Typically, this menu is used to enable the user to open the link in another window or tab. An add-on may enable opening in another application.

You can combine these values to add your menu item to multiple context menus by combining the values with a logical OR. For example, to add your menu to selected text or images, use the value 0x12 (18).

The second value that may be added to the context menu is the Flags entry. Despite the plural form of the name, there is currently only a single value available here. By adding the value 0x1 (1) to the Flags value, you will make the menu item run as though it were called using the showModalDialog method. This means that your script will run modally, separate from the browser.

The following shows an example of a simple add-on that searches for the selected text using Live Search:

  1. Create a new registry key under HKCU\Software\Microsoft\Internet Explorer\MenuExt.
  2. Name this new key &Live Search (or similar).
  3. Set the default value (on the right side of the regedit window) for this new key to point to a local Web page that will include the script for the context menu item. I used C:\Program Files\Internet Explorer\Plugins\livesearch.htm.
  4. Add a new DWORD value to the context menu item. Name this new value Contexts.
  5. Set the value of the Contexts value to 0x10. This enables this context menu for selected text only.

The code for the livesearch.htm file is as follows.

<script language="JavaScript">
  var parentwin = external.menuArguments;
  var doc = parentwin.document;
  var sel = doc.selection;
  var rng = sel.createRange();
  var str = new String(rng.text);

  if(0 < str.length)
  {
    window.open("http://search.live.com/results.aspx?q="
      + str, "_blank");
  }
</script>

Within the script you execute, you can access the parent window by accessing the external.menuArguments property. The code uses this to drill down into the document object to identify the selected text. It then uses this selected text as the query on Live Search. You can use the same code to search other search engines that provide a simple URL-style query.

Restart Internet Explorer and select some text on a Web page. Right-click to see the new item on the context menu.

Bb735853.IEAddOnsMenus_Fig02(en-us,VS.85).gif

Figure 2: New context menu in action

For more information, see Adding Entries to the Standard Context Menu

Creating DLL-based Menu Items

While menu items can easily be created with one or more registry items and an HTML page containing JavaScript, it does mean your code is available. In addition, it means your add-on is limited to the capabilities of JavaScript and HTML. You can implement more functionality by creating your menu add-on as a DLL. In order to create a DLL using this model, you must create a COM component. This component must implement IOleCommandTarget. In addition, if you want your add-on to be able to access the browser or its contents, you should also implement IObjectWithSite.

Note: You could use the .NET Framework to implement your add-on. However, due to the limitation of the browser hosting only a single version of the CLR, we will create the add-on using ATL.

The IOleCommandTarget interface contains two methods: QueryStatus and Exec. QueryStatus is used to determine the status of your menu item, such as when to disable it. It is called by the browser as the menu is activated (after your add-on has been initialized). The Exec method is called when your menu is selected; this is where you put the code for your add-on.

IObjectWithSite includes two methods: SetSite and GetSite. SetSite is called when your add-on is first initialized. You can use this method to initialize any pointers to the Web browser objects. GetSite can be used to retrieve this location later if needed. ATL makes using this interface easier by the addition of a template that provides a default implementation of the interface.

We will create an add-on that will download all of the images on the current Web page to a folder on your computer (see Figure 3). A folder browser control will enable the selection of the target folder.

Bb735853.IEAddOnsMenus_Fig03(en-us,VS.85).gif

Figure 3: Running the COM add-on

  1. Create a new ATL project using Visual Studio 2005.
  2. Right-click the project and select Add, Class.
  3. Select ATL Simple Object to start the Wizard.
  4. Give the new class a name (I selected ImageFetcher).
  5. Click Next.
  6. Set the options on the next page as shown in Figure 4.
Bb735853.IEAddOnsMenus_Fig04(en-us,VS.85).gif

Figure 4: Options for ImageFetcher class

The coding will involve changes to three of the generated files. First, add the following to the end of the RGS file for the project. This will enable the registration of the DLL when you run the project.

HKCU
{
  NoRemove SOFTWARE
  {
    NoRemove Microsoft
    {
      NoRemove 'Internet Explorer'
      {
        NoRemove Extensions
        {
          ForceRemove '{C688820C-4501-4296-9328-0250EC7B67DD}'
          {
            val 'CLSID' = s '{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}'
            val 'MenuText' = s 'Download &Images'
            val 'ClsIDExtension' = 
              s '{5331FC45-E103-422D-9E3C-33B5BE2DBD23}'
          }
        }
      }
    }
  }
}

The registration script will add the add-on to the current user hive, so that it will be usable only by the current user. The GUID in the line beginning with ForceRemove is the GUID for the library. I chose this because I knew it should be unique. The GUID for the ClsIDExtension value is the GUID for the CImageFetcher class. This is the class implementing IOleCommandTarget that represents the menu item. Since it adds entries under the Extensions key, this add-on will appear under the Tools menu.

Next, add the following include statements for libraries the project will use to the top of the ImageFetcher.h header file.

#include <shlguid.h>
#include <docobj.h>
#include <mshtml.h>
#include <urlmon.h>
#include <shlobj.h>

The urlmon.h header points to a library that is not included by default with Visual Studio 2005. You may need to include urlmon.lib in the additional dependencies for the project (see Figure 5).

Bb735853.IEAddOnsMenus_Fig05(en-us,VS.85).gif

Figure 5: Adding urlmon.lib to the project

Add the declarations for the methods and objects you will be using.

public:
STDMETHOD(SetSite)(IUnknown *pUnkSite);
STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, 
                       ULONG cCmds, 
                       OLECMD *prgCmds, 
                       OLECMDTEXT *pCmdText);
STDMETHOD(Exec)(const GUID *pguidCmdGroup, 
                DWORD nCmdID, 
                DWORD nCmdExecOpt, 
                VARIANTARG *pvaIn, 
                VARIANTARG *pvaOut );
private:
    CComPtr<IWebBrowser2> m_pBrowser;
    CComQIPtr<IOleCommandTarget, 
        &IID_IOleCommandTarget> m_pTarget;
    bool GetDownloadPath(TCHAR *pPath);
    void DownloadImages(IHTMLDocument2 *pDoc, 
        TCHAR *pPath);
};

The IObjectWithSite::SetSite method is called when the menu item is first activated and when it is being disposed of. The browser calls this method, passing in the IUnknown pointer for itself. You can cast this pointer to the types you need and store them for later use. If the pointer is NULL, that means that the browser is closing, and you should release any objects you saved here.

STDMETHODIMP CImageFetcher::SetSite(IUnknown* pUnkSite) {

    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        CComQIPtr<IServiceProvider> sp = pUnkSite;
        HRESULT hr = sp->QueryService(IID_IWebBrowserApp, 
          IID_IWebBrowser2, (void**)&m_pBrowser);
        hr = sp->QueryInterface(IID_IOleCommandTarget,
          (void**)&m_pTarget);
    } 
    else 
    {
        // Release cached pointers and other resources here.
        m_pBrowser.Release();
        m_pTarget.Release();
    }

    // Call base class implementation.
    return IObjectWithSiteImpl<CImageFetcher>::SetSite(pUnkSite);
}

The heart of the menu item is in the implementation of IOleCommandTarget.

STDMETHODIMP CImageFetcher::QueryStatus(const GUID *pguidCmdGroup, 
  ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText) {
    return m_pTarget->QueryStatus(pguidCmdGroup, 
      cCmds, prgCmds, pCmdText);
}

STDMETHODIMP CImageFetcher::Exec(const GUID *pguidCmdGroup, 
  DWORD nCmdID, DWORD nCmdExecOpt, 
  VARIANTARG *pvaIn, VARIANTARG *pvaOut) {

    // Get download path.
    TCHAR path[MAX_PATH];

    if (!GetDownloadPath(path))
    {
      return E_ABORT;
    }

    // Download images.
    CComPtr<IDispatch> spDoc;
    HRESULT hr  = m_pBrowser->get_Document(&spDoc);

    if(SUCCEEDED(hr))
    {
        CComQIPtr<IHTMLDocument2> spHTMLDoc = spDoc;

        if(NULL != spHTMLDoc)
        {
            DownloadImages(spHTMLDoc, path);
        }
    }

    return hr;
}

The QueryStatus method is implemented very simply in this class. It defers implementation to the browser itself using the m_pTarget pointer stored earlier.

The Exec method provides the two main parts of the add-on. First, it enables the user to select a target directory for the images. This uses the SHBrowseForFolder API to display a dialog. Finally, it downloads all available images from the current page to that directory.

void CImageFetcher::GetDownloadPath(TCHAR *pPath) {
    BROWSEINFO bi = { 0 };
    bi.lpszTitle = _T("Select the image destination");
    LPITEMIDLIST pidl = SHBrowseForFolder(&bi);

    if ( pidl != 0 )
    {
        // Get the name of the folder
        SHGetPathFromIDList (pidl, pPath);
        
        // Free memory used
        IMalloc * imalloc = 0;

        if (SUCCEEDED(SHGetMalloc(&imalloc)))
        {
            imalloc->Free(pidl);
            imalloc->Release();
        }

        return true;
    }

    return false;
}

void CImageFetcher::DownloadImages(IHTMLDocument2 *pDoc, 
  TCHAR *pPath) {
    CComPtr<IHTMLElementCollection> spImages;
    HRESULT hr = pDoc->get_images(&spImages);

    if(SUCCEEDED(hr) && NULL != spImages)
    {
        long count = 0;
        hr = spImages->get_length(&count);
        
        for(int i=0;i<count;i++)
        {
            CComVariant svItemIndex(i);
            CComVariant svEmpty;
            CComPtr<IDispatch> spImage;

            hr = spImages->item(svItemIndex, svEmpty, &spImage);
            
            if(SUCCEEDED(hr) && NULL != spImage)
            {
                CComQIPtr<IHTMLImgElement> spImg = spImage;
                CComBSTR src;
                hr = spImg->get_src(&src);
                
                if(SUCCEEDED(hr))
                {
                    // Extract filename
                    CComBSTR slash = _tcsrchr(src, '/');
                    
                    if(slash)
                    {
                        // Build local path
                        CComBSTR local = pPath;
                        local.Append("\\");
                        local.Append(slash + 1);

                        // Download and save file
                        hr = URLDownloadToFile(NULL, src, local, 
                          0, NULL);
                    }
                }
            }
        }
    }

The code uses the get_images method to retrieve the collection of images from the current document. It extracts the filename from the URL stored in the src attribute of the image and combines it with the path selected by the user. Finally, the URLDownloadToFile method is used to download the image. The result is a directory full of images. Figure 6 shows the result of running the add-on on the Coding4Fun section of MSDN.

Bb735853.IEAddOnsMenus_Fig06(en-us,VS.85).gif

Figure 6: Downloaded images

Conclusion

Every add-on needs some way to initiate it, and for many add-ons that means you'll have a menu item. Internet Explorer provides three main methods for you to create these menu items.

At the core of any of the three methods is the registry, and for the simplest of add-ons, it is all you really need. More flexible and capable add-ons can be created with a little JavaScript and a few registry settings. While creating an add-on as a DLL is the most labor-intensive of the three methods, it is also the most powerful. This method provides access to the full breadth of the Windows API, enabling your add-on to display dialogs or perform processing on the Web page.

Other Resources

Adding Entries to the Standard Context Menu
Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.