扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:Tony Schreiner,John Sudds 来源:微软 2007年10月16日
关键字:
为了进行快速测试,请在 SetSite 中设置一个断点,然后按 F5 启动调试程序。当出现“调试会话的可执行文件”对话框时,选择“默认的 Web 浏览器”,然后单击“确定”。如果 Internet Explorer 不是您的默认浏览器,则可以浏览查找可执行文件。
注意 在 Windows Vista 上,Internet Explorer 的“保护模式”功能将启动另一个进程,然后退出,这样会给调试带来一点难度。您可以通过以下两种方式轻松关闭当前会话的“保护模式”:从管理进程(例如 Visual Studio)启动浏览器,或者创建一个本地 HTML 文件并将其指定为 Internet Explorer 的命令行参数。
浏览器启动时,将加载 BHO 的 DLL。命中断点时,请注意是否设置了 pUnkSite 参数。再次按 F5 以继续加载主页。
关闭浏览器以验证是否通过 NULL 再次调用了 SetSite。
对事件做出响应
既然已经确认了 Internet Explorer 可以加载和运行 BHO,那就让我们在所举示例的基础上再深入一些,将 BHO 扩展到响应浏览器事件。在本部分中,我们介绍如何使用 ATL 为 DocumentComplete(在页面加载后显示一个消息框)实现一个事件处理程序。
为接到事件通知,BHO 建立一个与浏览器之间的连接点;为响应这些事件,它将实现 IDispatch。根据 DocumentComplete 的文档,该事件有两个参数:pDisp(IDispatch 的指针)和 pUrl。这些参数将作为事件的一部分传递给 IDispatch::Invoke;但手动析取这些事件参数并非一项简单的任务,并且易于出错。幸好 ATL 提供了一个默认实现,可以帮助简化这个事件处理逻辑。
HelloWorldBHO.h
首先通过包含 exdispid.h(为浏览器事件定义调度 ID)处理 HelloWorldBHO.h。
#include
接下来,从 IDispEventImpl 基类进行派生,该基类为处理事件提供了除 Invoke 之外的另一个简单安全的替代方法。IDispEventImpl 与事件汇映射配合工作,以将事件路由到相应的处理程序函数。我们明确说明,想要使用以下类定义(突出显示)处理由 DWebBrowserEvents2 接口定义的事件。
以下是引用片段: class ATL_NO_VTABLE CHelloWorldBHO : public CComObjectRootEx, public CComCoClass, public IObjectWithSiteImpl, public IDispatchImpl, public IDispEventImpl<1, CHelloWorldBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1> |
接下来,添加将事件路由到新的 OnDocumentComplete 事件处理程序方法的 ATL 宏,该事件处理程序方法采用的是 DocumentComplete 事件所定义的相同参数和顺序。将以下代码放置到该类的公共部分。
以下是引用片段: BEGIN_SINK_MAP(CHelloWorldBHO) SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) END_SINK_MAP() // DWebBrowserEvents2 void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL); |
提供给 SINK_ENTRY_EX 宏 (1) 的数字指的是 IDispEventImpl 类定义的第一个参数,在必要时用于区分来自不同接口的事件。另请注意,不能从该事件处理程序返回值;这是因为 Internet Explorer 无论怎样都会忽略从 Invoke 返回的值。
最后,添加一个专用成员变量,以跟踪各对象是否已建立了与浏览器的连接。
以下是引用片段: private: BOOL m_fAdvised; HelloWorldBHO.cpp |
要通过事件映射将事件处理程序连接到浏览器,可在处理 SetSite 期间调用 DispEventAdvise。同样,使用 DispEventUnadvise 断开连接。
以下是 SetSite 的新实现:
以下是引用片段: STDMETHODIMP CHelloWorldBHO::SetSite(IUnknown* pUnkSite) { if (pUnkSite != NULL) { // 缓存指向 IWebBrowser2 的指针。 HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser); if (SUCCEEDED(hr)) { // 注册以从 DWebBrowserEvents2 中汇集事件。 hr = DispEventAdvise(m_spWebBrowser); if (SUCCEEDED(hr)) { m_fAdvised = TRUE; } } } else { // 取消注册事件汇。 if (m_fAdvised) { DispEventUnadvise(m_spWebBrowser); m_fAdvised = FALSE; } // 在此释放缓存的指针和其他资源。 m_spWebBrowser.Release(); } // 调用基类实现。 return IObjectWithSiteImpl::SetSite(pUnkSite); } |
最后,添加一个简单的 OnDocumentComplete 事件处理程序。
以下是引用片段: void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL) { // 从站点检索顶级窗口。 HWND hwnd; HRESULT hr = m_spWebBrowser->get_HWND((LONG_PTR*)&hwnd); if (SUCCEEDED(hr)) { // 加载页面时输出消息框。 MessageBox(hwnd, L"大家好!", L"BHO", MB_OK); } } |
请注意,消息框会将站点的顶层窗口用作其父窗口,而不仅仅是通过该参数传递 NULL。在 Internet Explorer 6 中,NULL 父窗口并不阻止应用程序,也就是说,在消息框等待用户输入时用户可以继续与浏览器交互。在某些情况下,这会导致浏览器挂起或崩溃。在 BHO 需要显示 UI 的这种少见情况下,应始终通过指定指向父窗口的句柄来确保该对话框为应用程序模态。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者