关键字:WebBrowser, Internet Explorer, Custom Context Menu, ShowContextMenu, IDocHostUIHandler
1、概述 Internet Explorer提供了非常开发的接口,使开发人员不仅可以把其浏览器核心嵌入应用程序,还可以通过各种接口以实现更深层的控制。本文就将介绍对浏览器进行高级控制的话题之一——自定义上下文菜单。
2、最简单的情况 自定义的IE及WebBrowser的上下文菜单,最简单的方式就是在注册表的HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt下添加自定义的键值,步骤如下: 1)添加一个新的键,其名称即为将来显示在上下文菜单中的菜单项名称,如: HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\&Google Search 2)将新增的键的默认值设置为一个包含脚本的网页的URL(或文件路径全名),该网页中的脚本将在用户点击上下文菜单中的“Google Search”后被浏览器执行。
3)在新增的键下还可以新建一个二进制值Contexts,用以指定我们新增的菜单项针对特定的网页对象是否出现,其取值可以是如下值的组合(逻辑或)
Context Value Default 0x1 Images 0x2 Controls 0x4 Tables 0x8 Text selection 0x10 Anchor 0x20
4)还可以建立一个DWORD类型的Flags项并将其值设置为0x01,这将使得前述脚本在一个模态窗口中执行,就好像是通过window.showModalDialog调用的,但不同的是在脚本中仍然可以访问window对象。 5)实例脚本如下:
<SCRIPT LANGUAGE="JavaScript" defer> open(external.menuArguments.location.href); </script>
通过修改注册表自定义菜单的方法适用于Internet Explorer和WebBrowser,也具有良好的扩展性。但我们如果希望执行的是不仅仅是脚本,二是自己的程序中代码,这种方法就不适用了。
3、使用完全自定义的菜单 1)IDocHostUIhandler接口提供了一个ShowContextMenu方法,在需要显示上下文菜单之前,MSHTML引擎就会调用实现了IDocHostUIHandler接口的 宿主程序的ShowContextMenu方法。
HRESULTIDocHostUIHandler::ShowContextMenu( DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved );
dwID参数的意义与Contexts的组合类似;ppt为菜单的弹出点屏幕坐标;pcmdtReserved接口指向IOleCommandTarget接口,可用于检测网页对象的状态和执行命令等操作。pdispReserved在IE5以上版本中指向的是网页对象的IDispatch接口,用以区分不同对象,比如我们可以这样来获得网页对象的指针:
IHTMLElement *pElem; HRESULT hr; hr = pdispReserved->QueryInterface(IID_IHTMLElement, (void**)pElem); if(SUCCEEDED (hr)) { BSTR bstr; pElem->get_tagName(bstr); USES_CONVERSION; ATLTRACE("TagName:%s\n", OLE2T(bstr)); SysFreeString(bstr); pElem->Release(); }
如果我们在该方法中返回S_OK,则告诉MSHTML我们将使用自己的菜单(界面),如果是S_FALSE,则弹出默认的菜单。
2)实现 原理清楚之后,实现起来非常简单,和弹出一般的菜单没什么两样,举例如下,显示主框架的“文件菜单”:
HRESULT CMyHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt, IUnknown * pcmdtReserved, IDispatch *) { // 载入主菜单 HMENU hMenuParent = ::LoadMenu( ::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME) ); if (hMenuParent) { HMENU hMenu = ::GetSubMenu( hMenuParent, 0 ); // 取得“文件”子菜单 if (hMenu) { // 显示菜单 TrackPopupMenuEx( hMenu, TPM_LEFTALIGN | TPM_TOPALIGN, ppt->x, ppt->y, ::AfxGetMainWnd()->m_hWnd, NULL ); } ::DestroyMenu( hMenuParent ); } return S_OK; }
4、自定义标准上下文菜单 1)原理 更多的时候我们希望能在浏览器原来菜单的基础上作一些修改,如删掉“查看源文件”,添加自己的菜单项,等等,而不是完全不要原始的菜单,怎么办呢?借助MSDN提供的例子,我们来看看:
HRESULT CBrowserHost::ShowContextMenu(DWORD dwID, POINT *ppt,IUnknown *pcmdTarget,IDispatch *pdispObject) { #define IDR_BROWSE_CONTEXT_MENU 24641 #define IDR_FORM_CONTEXT_MENU 24640 #define SHDVID_GETMIMECSETMENU 27 #define SHDVID_ADDMENUEXTENSIONS 53
HRESULT hr; HINSTANCE hinstSHDOCLC; HWND hwnd; HMENU hMenu;
CComPtr<IOleCommandTarget> spCT; CComPtr<IOleWindow> spWnd;
MENUITEMINFO mii = {0}; CComVariant var, var1, var2; hr = pcmdTarget->QueryInterface(IID_IOleCommandTarget, (void**)&spCT); hr = pcmdTarget->QueryInterface(IID_IOleWindow, (void**)&spWnd); hr = spWnd->GetWindow(&hwnd);//取得浏览器窗口句柄 hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL")); if (hinstSHDOCLC == NULL) { // Error loading module -- fail as securely as possible return; } hMenu = LoadMenu(hinstSHDOCLC, MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU)); hMenu = GetSubMenu(hMenu, dwID); // Get the language submenu hr = spCT->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var); mii.cbSize = sizeof(mii); mii.fMask = MIIM_SUBMENU; mii.hSubMenu = (HMENU) var.byref; // Add language submenu to Encoding context item SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii); // Insert Shortcut Menu Extensions from registry V_VT(&var1) = VT_INT_PTR; V_BYREF(&var1) = hMenu; V_VT(&var2) = VT_I4; V_I4(&var2) = dwID; hr = spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2); // Remove View Source DeleteMenu(hMenu, IDM_VIEWSOURCE, MF_BYCOMMAND);//删除“查看源文件”菜单项 // Show shortcut menu int iSelection = ::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,//返回用户选择的菜单命令ID ppt->x, ppt->y, 0, hwnd, (RECT*)NULL); // Send selected shortcut menu item command to shell LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);//发送到Internet Explorer_Server进行内部处理。 FreeLibrary(hinstSHDOCLC); return S_OK; }
从上面的例子我们看出,基本的方法就是根据“shdoclc.dll”文件中的菜单资源建立菜单,再通过来自pcmdTarget的IOlcCommandTarget接口获得“编码”菜单以及HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt下的定义扩展菜单,然后以TPM_RETURNCMD标志调用TrackPopupMenu或TrackPopupMenuEx弹出菜单,并将返回的菜单命令ID教给浏览器窗口进行处理。这种方法可以调用许多通过浏览器无法直接调用的命令和对话框(参阅:《Internet Explorer 编程简述(五)调用IE隐藏的命令》)。
所以,我们只需要在弹出菜单之前做一些自定义操作即可达到修改默认菜单的目的,如上面代码中就用删除了“查看源文件”菜单项。
2)问题 如果我们不仅仅是删除默认的菜单项或是修改了默认的菜单项,还添加了自己的菜单项,会出现什么情况呢?由于使用了类似于MF [1] [2] 下一页 [聊天工具]用Windows98第二版直接共享Internet [系统软件]Internet Explorer 编程简述(繁体版) [系统软件]单击链接后无法打开新的 Internet Explorer 窗口或… [系统软件]delphi2005帮助系统使用了microsoft document exp… [系统软件]对Internet Explorer Web 控件做一点修改 [系统软件]OLE with the internet explorer [系统软件]打造个性化的Internet Explorer [系统软件]3dMax导出.x文件以及3D Explorer [常用软件]微软Internet Explorer 8最新动向 [常用软件]PDF全能管家——PDFExplorer
|