Browser Helper Objects: The Browser the Way You Want It

Browser Helper Objects: The Browser the Way You Want It

本文翻译自 这篇文章

本文描述如何使用 BHO 来自定义浏览器。主要包含如下几个章节:

  • Introduction
  • Program Customization
  • What Are Browser Helper Objects?
  • The Lifecycle of Helper Objects
  • The IObjectWithSite Interface
  • Writing a Browser Helper Object
  • Detecting Who's Calling
  • Getting in Touch with WebBrowser
  • Getting Events from the Browser
  • Accessing the Document Object
  • Managing the Code Window
  • Registration of Helper Objects
  • Summary

Introduction

在某些情况下,你可能需要一个定制版本的浏览器。要做到这一点,你可以在 WebBrowser 控件之上定制一个模块,这个模块要处理按钮、标签、及任何 ui 所需要的元素。然后,你就可以自由地添加你所希望的新特性。但这种做法很麻烦,实际上你要从头开始完成一个新的浏览器。因为 WebBrowser 只是一个浏览器解析引擎,除了它之外,你还得处理一大堆 UI 相关的任务,比如地址栏、任务栏、历史记录、状态栏、收藏夹等等。概括说来,用这种方法来完成浏览器的定制,你得做两部分的工作:先把 WebBrowser 封装成一个类似于 IE 的浏览器,然后在这之上添加你所需要的新特性。有没有啥办法可以省略第一步,直接对 IE 进行定制呢? Browser Helper Objects(BHO) 就能做到这一点。

Program Customization

在以往的经验中,对程序进行定制的首选方法是子类化技术。也就是说你可以修改程序窗口的消息处理函数来改变程序的行为。这种方法其实有点儿粗暴,因为被修改者完全不知道这些改动发生在了自己身上。

随着 Win32 API 的发展,子类化技术不再像以前那么方便使用了。如果你对指针无所畏惧,并且对于系统钩子的使用游刃有余的话,也许这些变化对你来说不算什么。但关键在于,每个 Win32 进程都运行在自己的进程空间,打破进程边界可能会造成一些问题。另一方面,程序本身可能会希望将可定制作为一个新特性来提供给外部使用。

对于后者,可定制的程序可以指定一个磁盘位置,其它程序把自己的组件放到这个位置里,然后可定制程序从这个地方搜索、装载、初始化其它程序的组件。这就是 IE 和它的 helper object 运行的机制。

What Are Browser Helper Objects?

从上述观点来看,Internet Explore 同其它 Win32 程序没什么区别,它们都有自己的内存控件。借助于 BHO,你可以编写自己的 进程内 COM 组件,IE 会在每次启动的时候加载它。组件被加载后,将同 IE 运行在同一个内存上下文中,因此它可以对 IE 窗口和模块做任何操作。例如,BHO 可以监视浏览器的常见事件,如 GoBack,GoForward,DocumentComplete 等;也可以访问并修改浏览器的菜单、工具栏;还在当前页面上创建一个窗口来显示一些附加信息;还可以安装 hook 来监听消息和浏览器动作。

在介绍更多内容前,有两点要先说明。首先,BHO 与浏览器主窗口相关联,这意味这浏览器每新开一个窗口,就会有一个 BHO 实例被创建,每当窗口被销毁,与之关联的 BHO 实例也会随之销毁。其次,BHO 只在 IE4.0 及以后的版本中可用。

在 Windows98, windows2000,或 WindowsNT 4.0 及以后的版本中,BHO 同样也可以用于 Windows Explore。

简单来说,BHO 是一种注册在特殊注册表项下面的 进程内 COM 组件。IE 每次启动时,都会去这个注册表项下查询,装载所有组件。浏览器会初始化组件,并向组件请求规定接口。如果能找到接口,IE 就会通过这些接口将它的 IUnknown 指针传递给 helper 对象。这个过程可以用下图说明:

Figure 1. How Internet Explorer loads and initializes helper objects. The BHO site is the COM interface used to establish a communication.

IE 启动时,可能会在注册表中找到很多需要装载的 CLSID,IE 会为每个 CLSID 创建一个进程内实例。这使得被装载的组建对象可以像原生对象那样对 IE 做任何事。但即使如此,要直接通过进程内实例去操纵 IE 还是太困难了,即使 BHO 可以在 IE 进程空间内子类化窗口,安装线程钩子,它还是不能干涉浏览器的核心活动。而如果想要做到这一点,势必需要建立一种在 IE 和 BHO 之间进行通信的机制。要做到这一点,BHO 应该实现 IObjectWithSite 接口,通过这个接口,IE 可以将它的 IUnknown 指针传递给 BHO。BHO 可以把这个 IE 指针存下来,并通过这个指针获取到更多 IE 接口,像是 IWebBrowser2,IDispatch, IConnectionPointContainer 等。

另一方面,你可以把 BHO 看作是 IE 的 shell extensions,就像 Windows shell extensions 那样。Windows shell extensions 是一种进程内 COM 组件,当用户对某个文档进行操作——例如,右键弹出菜单时,Window Explorer 会加载它。通过为这个 COM 组件编写一些接口,你可以添加一些新的菜单项到弹出菜单里。 shell extensions 也必须在注册表的某个特殊位置注册,这样 Windows Explorer 才能找到它。BHO 跟这个过程是类似的,只是要实现的接口不一样,装载的时机不一样。下面的表格展示了 Windows shell extensions 和 BHO 的区别和共同点:

如果你对 shell extension 感兴趣,可以参考 MSDN。

The Lifecycle of Helper Objects

Browser Helper Object 在浏览器主窗口展示前被加载,在窗口被销毁后卸载。如果你打开了多个浏览器窗口,也将有多个 BHO 实例被创建。

另外,BHO 的加载是动态的。每次打开 Windows Explorer 窗口,或者 IE 窗口,他们的加载器都会从注册表中读取 CLSID 并加载。这意味着如果你在打开窗口前注册了新的 BHO 组件到注册表中,那么新打开的窗口将及时加载它。

The IObjectWithSite Interface

从高层来看,BHO 是一个 动态链接库 DLL,它能将自己附加到 IE 的新实例上。BHO 可以通过所在容器的 site 与浏览器交互。

简单来说,site 是一个中间对象,它存在于容器和被包含对象之间。通过 site,容器可以管理被包含对象的内容。container 和 contained object 之间的这种基于 site 的关系涉及到了接口的实现,比如在 container 端实现 IOleClientSite 接口,在 contained object 端实现 IOleObject 接口,通过调用 IOleObject 的函数,container 可以告知 contained object 它宿主的环境信息。

当 IE 作为 container 端的时候,同样也需要这种模型来跟 BHO 交互,但之前的 IOleClientSite 太厚重了,所以提供了一个简单轻量的接口 IObjectWithSite。它只提供两个函数:

// browser 调用这个函数来传入它的 IUnknown 指针。通常 BHO 会把这个指针存下来以后用
HRESULT SetSite(IUnknown* pUnkSite)

// 返回最后一次调用 SetSite 时所设置的 IUnkonwn 指针
HRESULT GetSite(REFIID riid,void** ppvSite)

IObjectWithSite 接口的这两个函数是 BHO 必须实现的。注意不要让这两个函数返回 E_NOTIMPL 错误,你必须实现它。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,024评论 4 62
  • 用于解决表格单元格内容过多时的美观问题,主要涉及到4句CSS样式: table-layout: fixed。由于t...
    流浪的三鮮餡阅读 947评论 0 1
  • 逼仄处 没有光 伸手是峭壁 坚硬冰凉 身在 幽暗 不惧不慌 有路 便有希望 有生命 就可见 明天的 太阳 劲步走 ...
    雪莉诗话阅读 269评论 2 12
  • 晴29/40°,体感温度30°,空气良,湿度69%,西南风2级,日出05:14日落18:57 昨天中午9点骑OFO...
    光锋容阅读 291评论 0 0