上篇文章学习了npruntime的例子程序,接下来迫不及待地想实现自己的一个插件了。我决定使用VS 2005来做。
新建了一个名为npgnet的工程,按照npruntime例子,新建了np_entry.cpp、npn_gate.cpp、npp_gate.cpp、npgnet.def四个文件,然后新建了一个类CGnetFFPlugin,并且把例子中的关键代码添加了进来(我删除了一些cplugin类中的函数实体代码,因为我实现的功能和例子中的无关)。编译后,将生成的npgnet.dll放到FF的plugins目录下,然后在地址栏键入about:plugins,我靠,竟然没有我的插件!怎么回事?三个导出函数我都按照标准写了啊?比较了一下文件,我的工程没有添加.rc和resource.h,可能是这个原因。
回到VS 2005,在资源面板添加了一个VERSION资源项,修改ProductName等资源项以后,和npruntime例子比较了一下,还差MIMEType。这个东东很重要,这个给我的感觉就是FF插件的身份证,FF就是靠这个东东来匹配和识别你的插件的。但是我不知道VS 2005中怎么添加一个VERSION的键值,所以我只好用EditPlus打开npgnet.rc,手动添加了MIMEType:application/mozilla-npgnet-scriptable-plugin。OK,现在.rc和resource.h都欧了,再编译,将生成的npgnet.dll放到FF的plugins目录下,然后在地址栏键入about:plugins,我靠,竟然还是没有我的插件!真费解啊!
头大……接下来我进行了一系列的代码比较和尝试,失败了N多次,这里就省略不说了。最后发现原因原来在这个.rc上面。我的这个.rc是在VS 2005中使用菜单命令添加的,默认语言是简体中文,而npruntime例子是英文,用文件比较工具比较了一下,codepage和部分代码的位置都不太一样。其实只要把npruntime的这个.rc文件替换我的这个,然后编译输出的dll,FF就可以识别了!究竟是什么原因呢?是我的.rc缺少了些什么东西?还是FF只能识别英文的.rc?先不打算研究那么多了,至少我的插件的关键点不在这个上面,后面我还有很多事情需要去做。只要能让FF认出来,那就好。
既然决定要写插件,就要先理解插件的概念,在这个页面上有很详细的介绍:https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Plug-in_Basics
下面的文字是我的一些阅读笔记和体会:
一、插件的加载过程
当一个页面打开时,如果该页面上有嵌入一个插件,浏览器将会做以下事情:
- 通过MIMEType检查是否有匹配插件
- 加载插件代码到内存
- 初始化插件
- 创建一个新的插件实例
插件可以在一个页面上被实例多个对象,也可以在同一时刻在不同的窗口中被实例化。当页面被关闭时,插件的实例就会被销毁。当最后一个实例被删除后,插件代码就会从内存中被卸载掉。
下面是插件内的函数调用过程:
- 如果插件是首次被载入内存,浏览器会调用插件的NP_Initialize方法。为了方便起见,所有的插件定义函数以“NPP”开头,所有的浏览器定义函数以“NPN”开头。
- 当浏览器创建插件实例时,会调用NPP_New方法。
- 当插件实例被删除时(如关闭页面、关闭窗口),会调用NPP_Destroy方法。
- 当最后一个实例被删除,插件从内存中卸载时,会调用NP_Shutdown方法。
二、插件检测
可以使用Javascript来检测一个插件是不是已经安装了,下面是测试代码:
function DetectFFPlugin()
{
var mimetype = navigator.mimeTypes["application/mozilla-npgnet-scriptable-plugin"];
if(mimetype)
{
var plugin = mimetype.enabledPlugin;
if(plugin)
{
document.writeln("Plugin had been installed and be enabled.");
}
}
else
{
document.writeln("Sorry, Plugin has NOT been installed.");
}
}
嗯,看到这里,觉得这个检测很有用。当检测用户尚未安装时,可以指导用户到哪哪哪去下载安装(转向一个漂亮点儿的页面),当检测已经安装了,就动态加载插件代码。不错。:)
三、插件结构概述
一个插件中的方法分为插件方法(Plug-in Methods)和浏览器方法(Browser Methods)。插件方法是你在插件中自己去执行的那些方法,以NPP为前缀命名。浏览器方法是被Gecko所执行的那些方法,以NPN为前缀命名。数据结构(Data Structures)以NP开头。
插件可分为有窗口和无窗口两种,不过文章中建议使用有窗口的,说这样会更稳定和易于控制。另外文中提到了可以将插件作为页面的一部分,并且可以使用HTML代码来控制插件的显示与否。
有两种方式可以使一个插件不可见:
1、如果你使用embed标签,可以使用其hidden属性,例如:<embed src="audiplay.aiff" type="audio/x-aiff" hidden="true">
2、如果你是用object标签,由于它没有hidden属性,你可以用CSS来完成隐藏:
object
{
visibility: visible;
}
object.hiddenObject
{
visibility: hidden !important;
width: 0px !important;
height: 0px !important;
margin: 0px !important;
padding: 0px !important;
border-style: none !important;
border-width: 0px !important;
max-width: 0px !important;
max-height: 0px !important;
}
<object data="audiplay.aiff" type="audio/x-aiff" class="hiddenObject"></object>
接下来文中介绍了object这个标签的使用。并且给了一个例子来说明ActiveX和插件如何融为一体来使用。
最后面的部分是对object和embed两种标签的各种属性的说明和举例。
值得一提的是,对于object和embed两种标签都可以在页面上嵌入一个插件。如何取舍呢?文章中有这么一段话:
Though the object
element is the preferred way to invoke plug-ins, the embed
element can be used for backward compatibility with Netscape 4.x browsers, and in cases where you specifically want to prompt the user to install a plug-in, because the default plug-in is only automatically invoked when you use the embed
element.
意思是说:尽管object是推荐使用的调用插件的方式,embed也可嵌入插件(Netscape 4.x以上的浏览器),但如果你想在用户没有安装插件时,提示他进行安装,那么就应该选择用embed,因为FF的默认插件系统仅仅在你使用embed标签的时候,才会自动帮助你完成这种提示用户安装的效果。
我看过以后的感觉是,object使用起来似乎比embed要复杂,起码针对FF这个浏览器是这样,虽然他是HTML W3C的标准。多数情况下使用embed就可以了,何况现在谁还在用Netscape 4.x以下版本的浏览器啊,您说是不是?:)
OK,通过这几天的学习,终于对FF的插件编写有了一个初步的感性认识。到目前为之一切还比较顺利。