Click here to Skip to main content
Click here to Skip to main content

Adding a drop-down menu to an IE Toolbar button

By , 20 Sep 2006
 

TestExtension menu

Introduction

This article explains how to add a drop-down menu to a toolbar button of Internet Explorer.

The procedure for adding a standard button to the IE Toolbar is well described at MSDN.

Microsoft provides only one CLSID for the button extension: {1FBA04EE-3024-11d2-8F1F-0000F87ABD16}. So, using this CLSID, we can't add a button different from a standard style, and trying to set a BTNS_DROPDOWN or BTNS_WHOLEDROPDOWN style will be ignored:

HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extensions\<Your GUID>
"CLSID"="{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}"
...

Some of the Internet Explorer built-in toolbar buttons (such as "Back" or "Forward"), and some buttons of applications from Microsoft placed on the Internet Explorer toolbar (MS Office, for example), have drop-down functionality.

Unfortunately, MSDN does not provide any documentation regarding the addition of drop-down buttons (the author hasn’t found any information for a long time) for the Internet Explorer toolbar. Probably, such a procedure requires deeper integration with Internet Explorer than the usual registry workaround.

This article solution implements a drop-down behavior of the Internet Explorer toolbar button.

General Steps

Since the IE7 tabbed browsing concept has been released, the Internet Explorer internal window structure has been changed considerably. The following illustration from Spy++ shows the structure of internal windows of Internet Explorer 6 and 7.

IE6 internal window structure

IE7 internal window structure

The first step is to determine the version of Internet Explorer.

  1. Detecting the IE version.

    The best place for this is the IObjectWithSite::SetSite() method, which will be called during the creation and termination of the object we are implementing here (meaning our test extension). While creating, the site object passed at SetSite() is non-zero, and allows us to get the pointer to the Web browser instance.

    STDMETHODIMP CMasterObject::SetSite(IUnknown *pUnkSite)
    {
        if(!pUnkSite)
        {
            ATLTRACE(_T("SetSite(): pUnkSite is NULL\n"));
        }
        else
        {
            HRESULT hRes = S_OK;
            CComPtr<IServiceProvider> spSP;
            hRes = pUnkSite->QueryInterface(&spSP);
            if(SUCCEEDED(hRes) && spSP)
                hRes = spSP->QueryService(IID_IWebBrowserApp, 
                                          &m_pWebBrowser2);
        }
        
        m_spUnkSite = pUnkSite;
        m_bIsIe7 = FALSE;
    
        if(pUnkSite)
        {
            CComPtr<IDispatch>        pDocDisp;
            CComQIPtr<IHTMLDocument2> pHtmlDoc2;
            CComQIPtr<IHTMLWindow2>   pWindow;
            CComQIPtr<IOmNavigator>   pNavigator;
    
            HRESULT hRes = m_pWebBrowser2->get_Document(&pDocDisp);
            if(SUCCEEDED(hRes) && pDocDisp)
            {
                hRes = pDocDisp->QueryInterface(IID_IHTMLDocument2, 
                                               (void**)&pHtmlDoc2);
                if(SUCCEEDED(hRes) && pHtmlDoc2)
                {
                    hRes = pHtmlDoc2->get_parentWindow(&pWindow);
                    if(SUCCEEDED(hRes) && pWindow)
                    {
                        hRes = pWindow->get_navigator(&pNavigator);
                        if(SUCCEEDED(hRes) && pNavigator)
                        {
                            CComBSTR bstrVersion;
                            hRes = 
                              pNavigator->get_appVersion(&bstrVersion);
                            if(SUCCEEDED(hRes))
                            {
                                CHAR szVersion[MAX_PATH];
                                memset(szVersion,0,MAX_PATH);
    
                                WideCharToMultiByte(CP_ACP, 0, 
                                          bstrVersion.m_str, 
                                          -1, szVersion, 
                                          MAX_PATH, NULL, NULL);
    
                                if(strstr(szVersion, "MSIE 7.") != 0)
                                    m_bIsIe7 = TRUE;
                            }
                        }
                    }
                }
            }
        }
    
        return S_OK;
    }
  2. Finding a handle of the Web browser window.

    This handle is required for the TrackPopupMenu function that we should use later. We can obtain it very easily for versions 5 and 6:

    long nBrowser = 0;
    m_pWebBrowser2->get_HWND(&nBrowser);
    HWND hWndParent = (HWND)nBrowser;
    ...

    But since beta1 of version 7, we have to find the active tab first and the corresponding browser window next:

    IE7 internal window structure

    HWND hWnd = GetWindow(hWndParent, GW_CHILD);
    ...
    
    if(hWnd)
    {
        TCHAR szClassName[MAX_PATH];
        while(hWnd)
        {
            memset(szClassName,0,MAX_PATH);
            GetClassName(hWnd, szClassName, MAX_PATH);
            if(_tcscmp(szClassName,_T("TabWindowClass"))==0)
            {
                // the active tab should be visible
                if(IsWindowVisible(hWnd))
                {
                    hWnd = GetWindow(hWnd, GW_CHILD);
                    while(hWnd)
                    {
                        memset(szClassName,0,MAX_PATH);
                        GetClassName(hWnd, szClassName, MAX_PATH);
    
                        if(_tcscmp(szClassName,_T("Shell DocObject View"))==0)
                        {
                            hWnd = FindWindowEx(hWnd, NULL, 
                                   _T("Internet Explorer_Server"), NULL);
                            if(hWnd) hWndIe7ActiveTab = hWnd;
                            break;
                        }
                        hWnd = GetWindow(hWnd, GW_HWNDNEXT);
                    }
                }
            }
            hWnd = GetWindow(hWnd, GW_HWNDNEXT);
        }
    }
    
    if(hWndIe7ActiveTab) hWndMenuParent = hWndIe7ActiveTab;

    Probably, when IE7 will be fully released, Microsoft will present a new IWebBrowser interface, and we can then determine which tab is active at the moment. But, currently, MSDN does not provide any such function. So, we have to enumerate all internal "TabWindowClass" windows and assume that the active tab should be visible.

  3. Finding a handle of the IE Toolbar window.

    We can obtain it very easily for all IE versions as shown below:

    hWndToolBar = WindowFromPoint(pt);
  4. Finding the button on-screen position.

    In this step, we should determine if the button was clicked by the mouse or selected from the chevron’s menu.

    IE7 chevron menu

    And, if the button was clicked, we should calculate the button’s rectangle for correct menu positioning:

    int nIDCommand = -1;
    BOOL bRightAlign = FALSE;
    if(hWndToolBar)
    {
        ScreenToClient(hWndToolBar,&pt);
        int nButton = (int)::SendMessage(hWndToolBar, 
                       TB_HITTEST, 0, (LPARAM)&pt);
        if(nButton>0)
        {
            TBBUTTON pTBBtn;
            memset(&pTBBtn,0,sizeof(TBBUTTON));
            if(::SendMessage(hWndToolBar, TB_GETBUTTON, 
                             nButton, (LPARAM)&pTBBtn))
            {
                nIDCommand = pTBBtn.idCommand;
                RECT rcButton;
                if(::SendMessage(hWndToolBar,TB_GETRECT,nIDCommand,
                                (LPARAM)&rcButton))
                {
                    pt.x = rcButton.left;
                    pt.y = rcButton.bottom;
                    ClientToScreen(hWndToolBar,&pt);
    
                    RECT rcWorkArea;
                    SystemParametersInfo(SPI_GETWORKAREA,0,
                                        (LPVOID)&rcWorkArea,0);
                    if(rcWorkArea.right-pt.x<150)
                    {
                        bRightAlign = TRUE;
                        pt.x = rcButton.right;
                        pt.y = rcButton.bottom;
                        ClientToScreen(hWndToolBar,&pt);
                    }
                }
            }
        }
        else
        {
            GetCursorPos(&pt);
            bIsChevron = TRUE;
        }
    }
  5. The last step. Display the button as pushed, and pop up the menu.
    // draw pressed button
    if(nIDCommand!=-1 && !bIsChevron)
        ::SendMessage(hWndToolBar, TB_PRESSBUTTON, 
                      nIDCommand,  MAKELPARAM(1,0));
    // popup the menu
    int nCommand = TrackPopupMenu(hMenuTrackPopup, nFlags, 
                   pt.x, pt.y, 0, hWndMenuParent, 0);
    // release the button
    if(nIDCommand!=-1 && !bIsChevron)
        ::SendMessage(hWndToolBar, TB_PRESSBUTTON, 
                      nIDCommand,  MAKELPARAM(0,0));
    
    switch (nCommand)
    {
        case ID_ITEM1:
            ...

This example does not create a natural drop-down button (missing a drop-down arrow, or a separate section), but can be used as a simple and quick solution to extend the functionality of a simple IE Toolbar button.

Anyway, I hope others find this code useful. Please feel free to report errors, issues, or requests.

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

About the Author

Igor Tolmachev
Software Developer (Senior)
Ukraine Ukraine
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralIts not workingmemberUtkal Ranjan7 Oct '09 - 0:09 
Its not working with VS 2005, WinXP SP2 and IE8 Frown | :(
I have a requirement of toolbar dropdown button with dynamic menu like shown here : http://msdn.microsoft.com/en-us/library/bb775452%28VS.85%29.aspx#dropdown[^]
GeneralRe: Its not workingmemberandrej33@yandex.ru23 Dec '09 - 23:56 
Please note us if you find a solution
GeneralIE8 supportmemberandrej33@yandex.ru7 Sep '09 - 8:28 
I'am getting unhandled exaption on SendMessage(TB_HITTEST) while running IE8. Does any one knew the reason or have workaround?
QuestionDoes this work for IE8/Vista ?memberHappyFunBall13 Jul '09 - 13:50 
I'm seeing the button, but no dropdown menu.
 

Smile | :) Big Grin | :-D Laugh | :laugh: Wink | ;) Poke tongue | ;-P Sniff | :^) Frown | :( Sigh | :sigh: D'Oh! | :doh: Cry | :(( Sleepy | :zzz: Shucks | :->
Blush | :O Roll eyes | :rolleyes: OMG | :OMG: WTF | :WTF: Mad | :mad: Confused | :confused: Unsure | :~ Hmmm | :| Dead | X| Suspicious | :suss: Cool | :cool: Rose | [Rose]
GeneralNeed Helpmemberjayshml18 Jan '09 - 23:22 
Hi,
 
Great work! Congrats!
 
How Can I use the same code for desktop application?
 
I need active Webbrowser object in tabbed webbrowser.
 
Thanks in advance.
GeneralGreat work !!memberbishwajeet17 Sep '08 - 2:25 
But I am new to IE Extensions. And I am Working On C# .If anyone have idea to do this in C#.
 
mail me at : bishu_473@yahoo.co.in
 

Thanks
Bishwajeet
GeneralI need help!memberpla12318 Aug '08 - 17:03 
It is very good ,but I want to get the details. Maybe,you can teach me step by step in a project with vs2008.
Thanks very much.
could you give me a e-mail to gg8_8@yahoo.com.cn?
Waiting for you.
GeneralNewbiememberTahir Raza17 Jul '08 - 1:20 
First of all accept my regards for this article.
 
I am new to IE7 development and would like to know some initial steps, as, what project type of VS2005 you used? can you please specify some points to setup a simple development environment?
QuestionDoes this makes difference for creating drop down Menu Itemmembervikrant kpr31 Aug '07 - 0:26 

http://msdn2.microsoft.com/en-us/library/ms672057.aspx[^]
 
i could not interpret it Frown | :-(
QuestionAdding DropMenu to IE Buttonmembervikrant kpr30 Aug '07 - 23:13 
Hey Friends
 
Did anybody got a way to add DropDown to the Internet Explorer button?
 
If not Try out this
 
Install SkyPe
 
Note that SkyPe button on IE has dropdown
 
Go to Registry & change the text of the button added by SKyPe
 
Now again have a look at the button added by skype
 
does it shows dropdown?
 
May be it would help in solving the DropDown Mystery Smile | :)
Regards

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 20 Sep 2006
Article Copyright 2006 by Igor Tolmachev
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid