1、- 1 -目录一,BHO 开发概述 .31.1 BHO 的用途及实际应用 .31.2 BHO 的工作原理及技术环境 .3二,框架设计 .42.1 构建 BHO 框架 .42.2 实现 IOBJECTWITHSITE 的接口方法 .5三,注销 SESSION .73.1 将 BHO 注册为浏览器的事件处理器 .73.2 监听浏览器关闭事件,并向 SERVER 发送跳转指令 .83.3 关闭 BHO 注销页面 .93.4 阻止文件浏览器加载 BHO.93.5 向注册表中注册 BHO 组件 .10四,打包发布 .104.1 BHO 打包和找到关联 DLL 文件 .104.2 编写自动安装脚本 INF
2、 文件 .114.3 用 CABARC 工具打包。 .12- 2 -一,BHO 开发概述1.1 BHO 的用途及实际应用BHO(Browser Help Objects) ,是实现了特定接口的 COM 组件。BHO 只适用于 Windows 系统下的 IE 浏览器和文件浏览器。开发好的 BHO 插件在注册表特定的位置注册好后,每当微软的浏览器启动,BHO 实例就会被创建。在浏览器工作的过程中,BHO 会接收到很多事件,比如浏览器浏览新的地址、前进或后退、生成新的窗口、浏览器退出等等。BHO 可以在这些事件的响应中实现与浏览器的交互。本文档中我们将通过编写一个 BHO 实例来详细讲解 BHO 开
3、发的一般流程。该实例的功能是:当用户成功登陆网站后,如果用户点击 IE 关闭按钮,BHO 将捕获 IE 关闭事件,同时向 Server 发送一个注销当前用户 Session 的请求。Server 注销后跳转到 BHO 注销页面,BHO 捕获到 BHO 注销页面时,关闭该页面。1.2 BHO 的工作原理及技术环境BHO 是 COM 组件,而且一定实现了 IObjectWithSite 接口。这些组件除了在注册表中注册为COM Server 外,还必须将它们的 CLSID 在 HKEY_LOCAL_MACHINESOFTWARE Windows CurrentVersion ExplorerBro
4、wser Helper Objects 下注册为子键。微软在设计浏览器的时- 3 -候,已经给这些组件预留了空间。每当浏览器启动时,浏览器会首先在上述注册表位置查看是否有注册的 BHO CLSID;如果有则分别创建一个实例,并对 BHO 实例进行初始化,建立交互连接。 (注:BHO 实例只有在创建它的浏览器窗口销毁时才被释放。 )图 1.1 演示了 BHO 的创建过程:图 1.1表 1.1 BHO 支持的操作系统一览及 IE 一览二,框架设计2.1 构建 BHO 框架 开发环境: Microsoft Visual C+ 6.0 步骤 1:建 立 一 个 工 作 区 (WorkSpace)。IE
5、 版本 操作系统版本 支持 BHO4.00 Windows 95 and Windows NT 4.0 仅 IE4.05.00 Windows2000 IE 和文件浏览器6.00 WindowsXP IE 和文件浏览器7.00 Vista IE 和文件浏览器- 4 - 步骤 2:在 工 作 区 中 , 建 立 一 个 ATL 工 程 (Project)。 示 例 程 序 叫 TEST, 并 选 择DLL 方 式 , 见 图 2.1。图 2.1 步骤 3:增加 ATL 对象类。在菜 单 InsertNew ATL Object.( 或 者 用 鼠 标 右 键 在 ClassView 卡 片 中
6、弹 出 菜 单 ) 并 选 择 Object 分 类 , 选 中 Internet Exploer 项 目 。 见 图 2.2。图 2.2 步骤 4:填写 COM 类的名称。只要输入 Short Name,其它自动生成。见图 2.3。- 5 -图 2.32.2 实现 IObjectWithSite 的接口方法 步骤 1:先定义几个成员变量:CComQIPtr mWebBrowser2, (需要加入#include “ExDisp.h“) ,用以保存浏览器组件的指针;DWORD mCookie,用以保存与浏览器的连接 ID。见图 2.4图 2.4(详见 MyBho.h 文件) 步骤 2:IObj
7、ectWithSite 有两个接口方法:SetSite 和 GetSite。我们只需重载 SetSite就行了。在 ImyBho 中增加函数声明 SetSite 函数。见图 2.5变量声明- 6 -图 2.5 步骤 3: 实现 IDispatch 接口方法。事件处理也就在 IDispatch:Invoke 中实现(各个事件的 ID 在 ExDispID.h 中定义) 。 BHO 可能会接收到很多事件,但我们只需要响应我们感兴趣的那一部分。首先在 ImyBho 中增加该函数的声明。见图 2.6图 2.6 步骤 4: ImyBho 添加 RegisterEventHandler(BOOL inAd
8、vise)方法声明,实现向 IE 注册和注销事件。详见附录代码。通过上面的步骤,一个基本的 BHO 对象框架已经建立起来了。下一节来我们将根据具体要实现的功能(向 Server 发送注销 Session 命令)对代码主要功能作说明。三,注销 Session3.1 将 BHO 注册为浏览器的事件处理器注册 BHO 的事件的工作主要在 SetSite 函数中来完成。首先保存浏览器指针到 mWebBrowser2 成员变量中去。然后调用 RegisterEventHandler 函数向浏览器注册为事件处理器。见图 3.1- 7 -图 3.1(详见 MyBho.cpp 文件)3.2 监听浏览器关闭事件
9、,并向 Server 发送跳转指令监听浏览器事件,全部在 Invoke 函数中来完成。当捕获了浏览器退出事件(DISPID_ONQUIT)/*说明:SetSite(IUnknown *pUnkSite)是 IObjectWithSite 接口的方法。功能:1.保存浏览器组件指针,2.BHO 向浏览器注册为事件处理器。*/STDMETHODIMP CMyBho:SetSite(IUnknown *pUnkSite)if (pUnkSite)/保存浏览器指针到 mWebBrowser2 成员变量中去。mWebBrowser2 = pUnkSite; if (mWebBrowser2)/向浏览器注册
10、为事件处理器。return RegisterEventHandler(TRUE);return E_FAIL;/*说明:自定义函数,用以注册/注销 BHO 事件。功能:根据传入的参数 TRUE/FALSE 来向浏览器注册/注销 BHO 事件。*/STDMETHODIMP CMyBho:RegisterEventHandler(BOOL inAdvise)CComPtr spCP;/ 得到浏览器的连接点CComQIPtr spCPC(mWebBrowser2);HRESULT hr = spCPC-FindConnectionPoint(DIID_DWebBrowserEvents2, if (
11、FAILED(hr)return hr;if (inAdvise)/ 向浏览器注册事件。hr = spCP-Advise(reinterpret_cast(this), else/ 向浏览器注销事件。spCP-Unadvise(mCookie);return hr; - 8 -时,首先取得页面的窗口名,判读是不是成功登陆页面。如果是,向 Server 的 LogOffAction 发送一组数据,通知 Server 是 BHO 注销事件。然后退出 IE。见图 3.2图 3.2(详见 MyBho.cpp 文件)3.3 关闭 BHO 注销页面当 Server 监听到 BHO 发来的注销命令时,Ser
12、ver 注销当前用户的 Session 后,跳转到一个专门的 BHO 注销页面(正常情况下是注销退出到登陆页面)。当 BHO 检测到 IE 已经跳转到 BHO 注销页面时,就将窗口关闭。从而使用户对于注销过程可以忽视。见图 3.3。case DISPID_ONQUIT:/取得当前页面的标题。BSTR lpHader = NULL;mWebBrowser2-get_LocationName(/如果是标题是“登录成功页面”的话,说明用户关闭的是已经登录的主页。if (strstr( “登录成功页面“ , OLE2CA(lpHader)/需要跳转到 Server 的 LogOutAction 中去的
13、 URL。LPTSTR hostUrl = _T(“http:/localhost:8080/LogOff/logout.do“);/向 Server 发送“action=BhoLogoff“,提示是 BHO 注销事件。LPTSTR postData = _T(“action=BhoLogoff“);/取得 POST 数据长度。int size = WideCharToMultiByte(CP_ACP, 0, A2COLE(postData), -1, 0, 0, 0, 0);/为调用 Navigate2 方法定义参数。VARIANT vURL;VARIANT vFlags;VARIANT v
14、PostData;VARIANT vHeaders;VARIANT vNull;/此处略去给参数赋值的代码,详见 MyBho.cpp 中的 Invoke 函数。./跳转函数,并向 Server 发送数据。mWebBrowser2-Navigate2(/注销 BHO 事件处理器。RegisterEventHandler(FALSE);break;- 9 -图 3.3(详见 MyBho.cpp 文件)3.4 阻止文件浏览器加载 BHO为了阻止文件浏览器加载 BHO,在 Dll 入口函数 DllMain 中加入了判断。见图 3.4图 3.4(详见 TEST.cpp 文件)3.5 向注册表中注册 BH
15、O 组件为了向注册表中注册 BHO 组件,还需要在 MyBho.rgs 中加入下面的代码。见图 3.5case DISPID_NAVIGATECOMPLETE2:/监听页面跳转完毕事件/参数合法性检查if (pDispParams-rgvarg0.vt = (VT_BYREF|VT_VARIANT)char * strurl;CComVariant varURL(*pDispParams-rgvarg0.pvarVal);varURL.ChangeType(VT_BSTR);strurl = OLE2A(varURL.bstrVal);/如果发现当前的 URL 是 BHO 注销页面,则关闭当前
16、页面if (strstr(“http:/localhost:8080/LogOff/BhoLogOut.jsp“, strurl)mWebBrowser2-Quit();break;BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)if (dwReason = DLL_PROCESS_ATTACH)TCHAR pszLoaderMAX_PATH;GetModuleFileName(NULL, pszLoader, MAX_PATH);_tcslwr(pszLoader);/检查是谁在载入
17、,如果是文件浏览器,则退出。if (_tcsstr(pszLoader, _T(“explorer.exe“) return FALSE;_Module.Init(ObjectMap, hInstance, DisableThreadLibraryCalls(hInstance);else if (dwReason = DLL_PROCESS_DETACH)_Module.Term();return TRUE; / okHKLMSOFTWAREMicrosoftWindowsCurrentVersionExplorerBrowser Helper ObjectsE5155767-23E5-41
18、42-A63B-4DA81B196C36- 10 -图 3.5(详见 MyBho.rgs 文件)到此,一个简单的注销 Seesion 的 BHO 插件开发完毕了,当然还要在根据具体情况,在Server 端加以判断才行。详细代码,请参考附录。四,打包发布经过前面的工作,这个 BHO 可以打包成 CAB 格式发布到网站了。当然还要在 CAB 包中编写一个自动安装的脚步。当用户登录网站,就可以自动安装插件了。下面是详细步骤。4.1 BHO 打包和找到关联 Dll 文件首先,把 BHO 打包成 Dll 文件。在 VC 的 Build 菜单中选择 Set Active Project Configura
19、tion 选项,然后再选中 Win32 Release MinDependency 选项,点击 OK。如果此时编译发布出现 error LNK2001 错误的话,请到 Project-Settings-C/C+中去除_ATL_MIN_CRT 这个预处理符号。好了,如果一切顺利的话。我们编译的 Dll 文件就存放到了我们的工程下的ReleaseMinDependency 目录中了。接下来,由于我们的 Dll 用到了一些 VC 自带的 Dll 库文件。而用户不一定有这些库文件,所有要找到到底用了哪些库文件。这里我们使用 VC 自带的工具 Dependency。 (.Microsoft Visual StudioCommonTools 目录下) 。使用它打开我们刚编译好的 Dll 文件,就可以看到和它关联的所有 Dll 文件。见图 4.1