C#开发BHO插件UrlTrack
最近忽然突发奇想,想统计一下我最经常上的网站是哪些,并且在这些网站上都停留了多久。为此决定写一个BHO插件来做这件事。
BHO(Browser Help Objects)是实现了特定接口(IObjectWithSite)的COM组件。开发好的BHO插件除了要在注册表中注册为COM Server外,还必须将它的CLSID在HKLMSOFTWARE...Browser Helper Objects下注册为子键。每当浏览器[1]启动时,首先会在上述注册表位置查看是否有注册的BHO CLSID,如果有则分别创建一个实例,并对BHO实例进行初始化。BHO实例运行在浏览器的地址空间内,能对可访问的对象(如窗口、模块等等)执行任何操作,且因为它依附于浏览器的主窗口,所以其生命周期与浏览器实例的生命周期一致。下图演示了BHO的创建过程:
下面就来介绍一下如何开发BHO插件。首先创建一个C#项目,类型为Class Library。然后将Class1.cs改名为IObjectWithSite.cs,还要给IObjectWithSite添加两个功能:GetSite和SetSite。
Public Interface Iobjectwithsite
{
[Preservesig]
Int Setsite([Marshalas(Unmanagedtype.Iunknown)]Object Site);
[Preservesig]
Int Getsite(Ref Guid Guid, Out Intptr Ppvsite);
}
添加一个cs文件UrlTrack.cs,并且实现IObjectWithSite接口。使用BHO还需要添加两个引用SHDocVw.dll和MSHTML.dll,可以在WindowsSystem32目录下找到。
在IObjectWithSite.cs中,还需要为我们的程序指出IE的GUID,使得它可以挂接(attach)到IE上
[
ComVisible(true),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")
]
另外,还需要给BHO程序分配一个GUID,这个可以通过System.Guid.NewGuid()方法得到
[
ComVisible(true),
Guid("e90da13b-117a-4178-8111-0f712da09ff9"),
ClassInterface(ClassInterfaceType.None)
]
在UrlTrack.cs中,我们还需要写两个方法用来DLL注册和移除注册
public static string BHOKEYNAME = @"SOFTWARE...Browser Helper Objects";
[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHO_KEY_NAME, true);
if (registryKey == null)
{
registryKey = Registry.LocalMachine.CreateSubKey(BHO_KEY_NAME);
}
string guid = type.GUID.ToString("B");
RegistryKey bhoKey = registryKey.OpenSubKey(guid, true);
if (bhoKey == null)
{
bhoKey = registryKey.CreateSubKey(guid);
}
// NoExplorer: dword = 1 prevents the BHO to be loaded by Explorer.exe
bhoKey.SetValue("NoExplorer", 1);
bhoKey.Close();
registryKey.Close();
}
[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHO_KEY_NAME, true);
string guid = type.GUID.ToString("B");
if (registryKey != null)
registryKey.DeleteSubKey(guid, false);
}
接下来就是实现具体的统计功能了。考虑一下,当输入网址后,我们需要记录下网址以及当前的时间;当在同一浏览窗口中切换网址时,不仅需要记录下网址和当前时间,还要设置前一个浏览记录的结束时间;并且在关闭浏览器时,也要记下结束时间。所以在SetSite中需要挂载NavigateComplete2和OnQuit事件。
private void NavigateComplete2(object pDisp, ref object URL)
{
string url = URL as string;
if (url.IndexOf("about:blank") >= 0)
{
return;
}
if (visitHists.Count > 0)
{
VisitHist currentHist = visitHists[visitHists.Count - 1];
if (currentHist.VisitUrl != url)
{
currentHist.EndTime = System.DateTime.Now;
}
else
{
return;
}
}
VisitHist newHist = new VisitHist();
newHist.StartTime = System.DateTime.Now;
newHist.VisitUrl = url;
visitHists.Add(newHist);
}
private void OnQuit()
{
if (visitHists.Count > 0)
{
VisitHist currentHist = visitHists[visitHists.Count - 1];
currentHist.EndTime = System.DateTime.Now;
}
// 输出统计记录
...
}
开始编译,然后就可以在bin目录下找到项目的dll文件了。在Console中使用regasm /codebase "UrlTrack.dll"注册dll。打开注册表,在HKLMSOFTWARE...Browser Helper Object可以看到多出了一个子项{E90DA13B-117A-4178-8111-0F712DA09FF9}。
需要注意的是,需要将AssemblyInfo.cs文件中的ComVisible属性设为true,否则在注册BHO时会得到这样的信息:
RegAsm : warning RA0000 : No types were registered.
更多的BHO资料可以看这里:Browser Extensions
[1] 在Windows操作系统上有两种浏览器:资源浏览器(explorer.exe,应用于文件系统)和Internet浏览器(IEXPLORE.EXE,应用于互联网资源)。


May 30th, 2008 at 2:04 am
为什么注册完了以后,不起作用呢?似乎这个BHO没被执行。。
June 3rd, 2008 at 5:01 am
是执行的上面的例子程序吗?如果是的话,你可以使用windbg.exe来查看输出信息。或者在"输出统计记录"处将记录写入的磁盘文件中。
September 11th, 2008 at 0:45 am
新手真的不知道怎么调试。如果可以的话,能附邮件。
May 23rd, 2009 at 14:15 pm
为什么,我的mshtml.dll添加引用不成功?
May 26th, 2009 at 19:20 pm
能不能给出更详细点的情况,怎么个不成功法。
July 29th, 2009 at 7:43 am
为什么我的BHO在IE8下的管理加载项中,显示发行者是:
(未验证)公司名称
另外是已经启用,但是没有作用,其他正常的加载项,能显示加载时间,而我的BHO没有显示加载时间。
July 31st, 2009 at 12:18 pm
另外我用的是Win2008,楼主你能在Win2008+IE8测试通过吗。
August 6th, 2009 at 22:50 pm
本文的例子就可以在Win2008+IE8下面测试通过。将下载的代码在VS2008中upgrade,然后修改install.bat中的路径后就可以了。加载时间也是显示的。至于发行者也显示未验证的,这是因为文件没有通过数字签名验证。
August 7th, 2009 at 7:38 am
你好,感谢你的回复,不好意思,是Win2003+VS2008+IE8.
我现在尝试安装了数字签名,现在在IE管理加载项没有看到(不信任)字样,不过还是没有效果,我在代码complete2中加了一句messageBox.show(url)。加载时也没有显示。
截图
http://www.chenjiliang.com/Temp/UrlTrack1.jpg
http://www.chenjiliang.com/Temp/UrlTrack2.jpg
August 8th, 2009 at 0:32 am
complete2中的messagebox必须在打开非about:blank网页后才会被调用。至于没有显示加载时间就不太清楚是什么原因了,在我这边一直是显示的。