为 IE 工具栏按钮添加下拉菜单






4.74/5 (14投票s)
2006年9月20日
3分钟阅读

145354

1811
本文介绍了如何为 Internet Explorer 的工具栏按钮添加下拉菜单。
引言
本文介绍了如何为 Internet Explorer 的工具栏按钮添加下拉菜单。
在 MSDN 上很好地描述了将标准按钮添加到 IE 工具栏的步骤。
Microsoft 仅为按钮扩展提供一个 CLSID:{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}。因此,使用此 CLSID,我们无法添加与标准样式不同的按钮,并且尝试设置 BTNS_DROPDOWN
或 BTNS_WHOLEDROPDOWN
样式将被忽略。
HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extensions\<Your GUID>
"CLSID"="{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}"
...
某些 Internet Explorer 内置的工具栏按钮(例如“后退”或“前进”),以及 Microsoft 应用程序放置在 Internet Explorer 工具栏上的某些按钮(例如 MS Office),都具有下拉功能。
不幸的是,MSDN 没有提供任何关于添加下拉按钮的文档(作者长期以来一直没有找到任何信息)给 Internet Explorer 工具栏。 也许,这样的过程需要比通常的注册表解决方法更深入地集成到 Internet Explorer 中。
本文的解决方案实现了 Internet Explorer 工具栏按钮的下拉行为。
通用步骤
自从 IE7 标签浏览概念发布以来,Internet Explorer 的内部窗口结构发生了很大变化。 以下来自 Spy++ 的插图显示了 Internet Explorer 6 和 7 的内部窗口结构。
第一步是确定 Internet Explorer 的版本。
- 检测 IE 版本。
为此,最好的地方是
IObjectWithSite::SetSite()
方法,该方法将在我们在此处实现的对象的创建和终止期间被调用(意思是我们的测试扩展)。 在创建期间,在SetSite()
处传递的站点对象是非零的,并且允许我们获取 Web 浏览器实例的指针。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; }
- 查找 Web 浏览器窗口的句柄。
此句柄是稍后应该使用的
TrackPopupMenu
函数所必需的。 对于版本 5 和 6,我们可以非常轻松地获得它long nBrowser = 0; m_pWebBrowser2->get_HWND(&nBrowser); HWND hWndParent = (HWND)nBrowser; ...
但是,从版本 7 的 beta1 开始,我们必须首先找到活动标签,然后找到对应的浏览器窗口
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;
也许,当 IE7 完全发布时,Microsoft 将提供一个新的
IWebBrowser
接口,然后我们可以确定哪个选项卡当前处于活动状态。 但是,目前,MSDN 并没有提供任何这样的功能。 因此,我们必须枚举所有内部的“TabWindowClass
”窗口,并假定活动选项卡应该是可见的。 - 查找 IE 工具栏窗口的句柄。
我们可以像下面显示的那样,非常轻松地获得所有 IE 版本的句柄
hWndToolBar = WindowFromPoint(pt);
- 查找按钮的屏幕位置。
在此步骤中,我们应该确定按钮是被鼠标单击还是从 chevron 的菜单中选择的。
并且,如果单击了该按钮,我们应该计算按钮的矩形以进行正确的菜单定位
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: ...
此示例没有创建自然的下拉按钮(缺少下拉箭头或单独的部分),但可以用作扩展简单 IE 工具栏按钮功能的简单快速的解决方案。
无论如何,我希望其他人发现此代码有用。 请随时报告错误、问题或请求。