[Ray Linn]用Visual Studio 2008开发IE BHO(浏览器帮助对象) 之三
程序员文章站
2022-05-25 16:05:50
...
接下来,我们要为IE增加一个按钮(注意不是toolbar,toolbar要复杂得多),基本这是一个注册表的魔术.打开RayBHO.rgs, 添加
当然,你也可以把这一项放在HKCU(Current User)下,这样的话,该Button只对当前用户起作用.
这些注册表项说明如下:
当然你也可以不使用COM来响应按钮的动作,另外两个键Exec和Script,可以设置响应的程序或者脚本..这个不是重点.
现在编译,然后从IE的自定义工具栏将这个按钮拖出来...如图所示:
点点看.....结果呢? 当然是不起作用!
因为除以上步骤外,该com 对象还必须实现 IOleCommandTarget接口。
IOleCommandTarget包含QueryStatus和Exec两个方法,其中QueryStatus方法会被IE调用来获得当前菜单的状态.当工具条按钮被点击时,com 对象的 IOleCommandTarget::exec 方法被调用,此时ncmdid 的值为 1;当菜单项被点击时,ncmdid 的值为 2。这样开发者就能区分工具条按钮和菜单项这两个不同操作
首先让CRayBHO继承IOleCommandTarget接口,最后我们得到这样得一个继承树
再COM_MAP里增加
最终得到
声明IOleCommandTarget的两个方法
然后实现之,基本我们的重点放在Exec方法上...所以先给出QueryStatus的实现
在Exce方法中,可以放入你的响应逻辑,或者更简单的,只是一个MessageBox响应. 这里我给出一段实际的Video Player的代码,这个代码可以为按钮添加下拉菜单,而这个菜单实际是保存在资源文件中的!并且你可以看到如何在Exce响应菜单上点击事件.
最后的效果如下:
很有意思的是, 我被认为是windows的粉丝,但其实我工作里搞的是linux的driver. 论坛里的风气常常喜欢比较A比较B,或者痛斥A痛斥B, 但你深入了解了么?
HKLM { NoRemove Software { NoRemove Microsoft { NoRemove 'Internet Explorer' { NoRemove Extensions { ForceRemove '{1AC31710-6759-484f-A129-A70C55485DA1}' { val ButtonText = s 'Hello,World' val Icon = s '%MODULE%,201' val HotIcon = s '%MODULE%,202' val CLSID = s '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}' val ClsidExtension = s '{057F3E68-6C2E-40A5-A641-E8CF9D6766F3}' val 'Default Visible' = s 'yes' } } } } } }
当然,你也可以把这一项放在HKCU(Current User)下,这样的话,该Button只对当前用户起作用.
这些注册表项说明如下:
ForceRemove '{1AC31710-6759-484f-A129-A70C55485DA1}' -- 该extersion的CLSID,请自己用GUID这个程序生成. val ButtonText = s 'Hello,World' // 按钮上的文字说明 val Icon = s '%MODULE%,201' // 按钮的图标,可以是icon的绝对路径,也可以和我的例子一样从资源文件里加载. val HotIcon = s '%MODULE%,202'// 鼠标悬停时按钮的图标,与Icon类似. val CLSID = s '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}' //该CLSID意思为可执行,此值有特定含义,请小心修改. val ClsidExtension = s '{057F3E68-6C2E-40A5-A641-E8CF9D6766F3}' // 这个是RayBHO的CLSID,即表示该按钮的动作连接到RayBHO这个com上,具体值有所不同,必须查询你自己的rgs文件得到. val 'Default Visible' = s 'yes'//按钮可见.
当然你也可以不使用COM来响应按钮的动作,另外两个键Exec和Script,可以设置响应的程序或者脚本..这个不是重点.
现在编译,然后从IE的自定义工具栏将这个按钮拖出来...如图所示:
点点看.....结果呢? 当然是不起作用!
因为除以上步骤外,该com 对象还必须实现 IOleCommandTarget接口。
IOleCommandTarget包含QueryStatus和Exec两个方法,其中QueryStatus方法会被IE调用来获得当前菜单的状态.当工具条按钮被点击时,com 对象的 IOleCommandTarget::exec 方法被调用,此时ncmdid 的值为 1;当菜单项被点击时,ncmdid 的值为 2。这样开发者就能区分工具条按钮和菜单项这两个不同操作
首先让CRayBHO继承IOleCommandTarget接口,最后我们得到这样得一个继承树
class ATL_NO_VTABLE CRayBHO : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CHelloWorldBHO, &CLSID_HelloWorldBHO>, public IObjectWithSiteImpl<CHelloWorldBHO>, public IDispatchImpl<IHelloWorldBHO, &IID_IHelloWorldBHO, &LIBID_HelloWorldPluginLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IDispEventImpl<1,CHelloWorldBHO,&DIID_DWebBrowserEvents2,&LIBID_SHDocVw,1,1>, public IOleCommandTarget
再COM_MAP里增加
COM_INTERFACE_ENTRY(IOleCommandTarget)
最终得到
BEGIN_COM_MAP(CHelloWorldBHO) COM_INTERFACE_ENTRY(IHelloWorldBHO) COM_INTERFACE_ENTRY(IObjectWithSite) COM_INTERFACE_ENTRY(IOleCommandTarget) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP()
声明IOleCommandTarget的两个方法
// IOleCommandTarget STDMETHOD(Exec)(const GUID*, DWORD nCmdID, DWORD, VARIANTARG*, VARIANTARG* pvaOut); STDMETHOD(QueryStatus)(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText);
然后实现之,基本我们的重点放在Exec方法上...所以先给出QueryStatus的实现
STDMETHODIMP CRayBHO::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText) { if (cCmds == 0) return E_INVALIDARG; if (prgCmds == 0) return E_POINTER; prgCmds[0].cmdf = OLECMDF_ENABLED; return S_OK; }
在Exce方法中,可以放入你的响应逻辑,或者更简单的,只是一个MessageBox响应. 这里我给出一段实际的Video Player的代码,这个代码可以为按钮添加下拉菜单,而这个菜单实际是保存在资源文件中的!并且你可以看到如何在Exce响应菜单上点击事件.
STDMETHODIMP CRayBHO::Exec(const GUID*, DWORD nCmdID, DWORD, VARIANTARG*, VARIANTARG*) { if(m_spUnkSite == 0 || m_spWebBrowser == 0) return S_OK; HRESULT hRes = S_OK; CComPtr<IDispatch> pDocDisp; CComQIPtr<IHTMLDocument2> pHtmlDoc2; hRes = m_spWebBrowser->get_Document(&pDocDisp); if(SUCCEEDED(hRes) && pDocDisp) { hRes = pDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc2); if(SUCCEEDED(hRes) && pHtmlDoc2) { SHANDLE_PTR nBrowser = 0; m_spWebBrowser->get_HWND(&nBrowser); HWND hWndParent = (HWND)nBrowser; POINT pt; GetCursorPos(&pt); HINSTANCE hInstance = _AtlBaseModule.GetModuleInstance(); HMENU hMenu = LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU_POPUP)); HMENU hMenuTrackPopup = GetSubMenu(hMenu, 0); if(hMenuTrackPopup && hWndParent) { BOOL bIsChevron = FALSE; HWND hWndMenuParent = NULL; HWND hWndToolBar = NULL; hWndMenuParent = hWndParent; hWndToolBar = WindowFromPoint(pt); if(m_bIsIe7) { HWND hWndIe7ActiveTab = hWndParent; HWND hWnd = GetWindow(hWndParent, GW_CHILD); // looking for the Internet Explorer_Server window // this window should be a parent for TrackPopupMenu 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; //strHWndMenuParent = _ltoa(hWndMenuParent, 10); } 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; } } UINT nFlags = TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTBUTTON; if(bRightAlign) nFlags |= TPM_RIGHTALIGN; else nFlags |= TPM_LEFTALIGN; // 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)); //CStringArray* m_EvNameArr = new CStringArray(); //m_EvNameArr->Add(strHWndMenuParent); BOOL bFound = FALSE; switch (nCommand) { case ID_CHIMP: { MessageBox(hWndParent,_T("Play Video"),_T("IEVideo --Ray"), MB_OK|MB_ICONEXCLAMATION); } break; case ID_SELECT: { MessageBox(hWndParent,_T("Select Video"),_T("IEVideo --Ray"), MB_OK|MB_ICONEXCLAMATION); } break; case ID_STOP: { MessageBox(hWndParent,_T("Stop Video"),_T("IEVideo --Ray"), MB_OK|MB_ICONEXCLAMATION); } break; case ID_ABOUT: { MessageBox(hWndParent,_T("About IEVideo"),_T("IEVideo --Ray"), MB_OK|MB_ICONEXCLAMATION); } break; } } } } return S_OK; }
最后的效果如下:
很有意思的是, 我被认为是windows的粉丝,但其实我工作里搞的是linux的driver. 论坛里的风气常常喜欢比较A比较B,或者痛斥A痛斥B, 但你深入了解了么?