`

[Ray Linn]用Visual Studio 2008开发IE BHO(浏览器帮助对象) 之三

阅读更多
接下来,我们要为IE增加一个按钮(注意不是toolbar,toolbar要复杂得多),基本这是一个注册表的魔术.打开RayBHO.rgs, 添加

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, 但你深入了解了么?
  • 大小: 32.2 KB
  • 大小: 19.3 KB
分享到:
评论
4 楼 NothingCanBeDone 2013-01-13  
楼主,你这Project,能放出来了,感激不尽。
3 楼 jcssdn 2009-07-23  
请教两个问题...嘿嘿...
按您的方法我加了两个按钮A和B...
1:) 单击后怎么区分单击了哪个按钮 (我想要的效果: 单击A后弹出MessageBox, 单击B后, 弹出下拉菜单)

2:) 怎么实现按钮A和B的右击事件...
2 楼 andywu 2009-03-15  
有人抢我sf。支持该系列,阅读中...
1 楼 yyliuliang 2009-03-11  
期待第四章

相关推荐

    【工作点滴】用VS 2010 编译 Ruby 1.9.1

    标题中的“【工作点滴】用VS 2010 编译 Ruby 1.9.1”表明了本文将探讨如何使用Visual Studio 2010这个集成开发环境(IDE)来编译Ruby 1.9.1版本的源代码。Ruby是一种动态、面向对象的编程语言,而1.9.1是它的一个...

    Kagney LInn

    a little bit huge FUcking guy this bitch web!

    模拟技术中的Wolfson使Linn的新产品Akurate DS展示音乐的未来、实现终极音乐体验

    标题中的核心知识点主要聚焦在模拟技术以及Wolfson(欧胜微电子)的WM8741音频数字模拟转换器在Linn新产品Akurate DS中的应用,这款产品被誉为展示音乐未来和实现终极音乐体验的关键。 模拟技术是音频系统中的重要...

    与短消息开发相关的GSM AT指令

    AT指令集 全球定位系统(GPS)系统由于性能优异,在民用上应用范围很广。其可以用于空中交通管理、大地精密测量、摄影测量、监测地壳运动、火山活动、野外调查和勘探的...本文主要介绍与短消息开发相关的GSM AT指令。

    linn-gaarder-js1-ca

    【标题】"linn-gaarder-js1-ca" 是一个与JavaScript编程相关的项目或课程,可能由Linn Gaarder创建,旨在教授基础到中级的JavaScript知识。这个项目的名称暗示了它可能涵盖JavaScript的第一个阶段,即初级部分,并且...

    linn9000cass:linn9000cass:用于 Linn9000 和 LinnSequencer 数据盒格式的工具-开源

    工具“l9000cassdecode”可用于对 Linn9000 和 LinnSequencer 的数据盒(磁带)格式进行解码。 根据数据盒内容,提取 Linn9000 相关文件,例如以下文件类型:DSQ、MSQ、ALL、ASQ、ADS、AMS、ALM、SND、KIT。 可选:...

    linn9000cass:linn9000cass:用于Linn9000和LinnSequencer数据盒带格式的工具-开源

    根据数据盒带的内容,提取与Linn9000相关的文件,例如,使用以下文件类型:DSQ,MSQ,ALL,ASQ,ADS,AMS,ALM,SND,KIT。 可选:tar文件导出。 注意:例如,可以通过包含在项目“ linn9000fs”中的工具“ l9000fs...

    Linn-Wilhelmsen-js1-ma1

    【标题】"Linn-Wilhelmsen-js1-ma1" 可能是指一个JavaScript编程项目或课程作业,由Linn和Wilhelmsen两位作者共同完成,第一部分(可能是系列中的第一个模块或者任务)。 【描述】由于提供的描述与标题相同,没有...

    Syngress.Coding for Penetration Testers.Building Better Tools.2011

    《Coding for Penetration Testers: Building Better Tools》是一本专注于渗透测试领域的书籍,通过教授如何使用脚本语言自行开发工具,帮助读者提高自身的技能水平。书中不仅介绍了基本概念和理论知识,还深入探讨...

    Volumio-BubbleUPnP-Server:用于Volumio的BubbleUPnP服务器安装程序

    用于Volumio的BubbleUPnP服务器安装程序 这是一个直接在Volumio系统上安装BubbleUPnP Server的帮助程序工具。 可用于流化潮汐和Qobuz(使用MDP)。 您需要的是OpenHome渲染... 由于,现在只能通过SSH安装第三方或开发

    sb2m3u:Linn Songbox 远程播放列表到 M3U 的转换

    在 Kinsky 中创建播放列表很棒,我想远程保存它们,如果您使用 Linn 的 Songbox,您可以这样做。 播放列表上的每个曲目都是一个 URI,它允许灵活地从多个媒体服务器创建播放列表,此外元数据的 URI 也与播放列表中的...

    Scala与Clojure函数式编程

    函数式编程是编程范式之一,它强调使用函数来表达计算,而非传统命令式的编程方式。在函数式编程中,数据和函数结合得非常紧密,数据被视为函数作用的原象,而函数则是数据的变换器。函数式编程在逻辑上摒弃了可变...

    [Pragmatic Bookshelf] Functional Programming Patterns in Scala and Clojure

    - **对象导向编程(Object-Oriented Programming, OOP)简介**:对象导向编程是一种编程方法论,它通过对象来组织代码。这些对象由类定义,并包含属性和行为。OOP强调封装(Encapsulation)、继承(Inheritance)、...

    D-Link路由器当交换机配置步骤.doc

    【路由器作为交换机的配置方法】 在某些网络环境中,我们可能需要将一个路由器用作交换机,以便扩展网络接口或管理多个设备的连接。这里,我们将详细解释如何将D-Link路由器转换为交换机,以及如何配置不同品牌的...

    Phaseolus lunatus Linn vel aff的种子中A型血专-性凝集素的研究* (1991年)

    用亲和层析法从PhaseoZus lunatus Linn vel aff的种子中纯化出一种对人类A型血专一的凝集素.该凝集素的粗浸提液都只对A型血细胞凝集,而对 B、O型绝不凝集.当纯化的凝集素浓度为0.98μg/mL时,即能凝集A型血细胞,...

    4万元HIEND解码器LINNAKURATEDS评测汇编.pdf

    【LINN AKURATE DS评测】 LINN是一家享有盛誉的HI-END音响品牌,创立于1972年,以其卓越的音质和经典产品如Sondek LP12黑胶唱盘和CD12播放机而闻名。随着时代的发展,LINN宣布将停止CD播放机的生产,转而专注于PC...

    4万元HIEND解码器LINNAKURATEDS评测.pdf

    LINN DS系列的独特之处在于采用了网线接口,不同于传统的USB、火线、光纤或同轴接口。这种设计极大地保证了音频数据传输的精度,避免了计算机操作系统对音频的潜在干扰,从而实现近乎无损的数据传输。USB接口可能会...

    基于连接自组织发育的稀疏跨越-侧抑制神经网络设计.pdf

    本文主要探讨了一种新颖的神经网络设计方法,即基于连接自组织发育的稀疏跨越-侧抑制神经网络(Sparse Span-lateral Inhibition Neural Network, sS-LINN)。这种网络设计受到生物神经系统中神经元稀疏连接特性的...

    黄芪属(Astragalus Linn)植物生物学特性及组织培养技术研究进展 (2006年)

    本文主要针对黄芪属(Astragalus Linn)植物的生物学特性和组织培养技术进行了全面的回顾与总结。黄芪作为传统中药材之一,在全球范围内拥有广泛的药用价值。近年来,随着生物科技的进步,黄芪属植物的研究逐渐深入...

    Windows 平台的64位 GCC 编译器

    快来体验MinGW64 for Windows 64,即x86_64-w64-mingw32,这是 GCC 在 Windows 平台上的移植,和 Cygwin 不...目前版本 4.7.0 ,该发布由我(ray_linn)编译,如发现任何问题,请到 http://mingw-w64.sf.net/上提问。

Global site tag (gtag.js) - Google Analytics