![]() |
Desktop Development »
Shell and IE programming »
IE Programming
Intermediate
Adding a drop-down menu to an IE Toolbar buttonBy Igor TolmachevThis article explains how to add a drop-down menu to a toolbar button of Internet Explorer. |
VC6, VC7, VC7.1, WindowsVS2005, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

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.
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.


The first step is to determine the version of Internet Explorer.
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;
}
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:

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.
We can obtain it very easily for all IE versions as shown below:
hWndToolBar = WindowFromPoint(pt);
In this step, we should determine if the button was clicked by the mouse or selected from the chevron�s 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; } }
// 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.
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 20 Sep 2006 Editor: Smitha Vijayan |
Copyright 2006 by Igor Tolmachev Everything else Copyright © CodeProject, 1999-2010 Web22 | Advertise on the Code Project |