NPAPI插件开发学习:NPAPI的运行流程

时间:2021-03-31 17:46:11

转载一篇介绍NPAPI运行流程的博文,原文出处:

http://hxis9e2q.i.sohu.com/blog/view/208654665.htm

Netscape Plugin Interface(NPAPI)

大致的说明可以看下官方文档Plugin

本文主要针对于javascript与插件交互部分做一些交流,比如用于数字证书的操作(淘宝和支付宝的插件),用于播放的flashplayer插件等

javascript的交互需要用到NPAPI中的npruntime Scriptingplugins

下面的部分将以示例的方式说明整个过程如何去实现

 在开始前需要从火狐浏览器源代码中获取接口头文件火狐4.0.1源码下载

下载后在\firefox-4.0.1.source\mozilla-2.0\modules\plugin可以找到一些samples和头文件,这里为方便下载,上传了一份单独的plugin文件夹

另外,基于NPAPI的一个跨浏览器插件开发的框架FireBreath,非常容易上手而且据说跨浏览器的支持非常好,但是非常笨重,有些功能不需要的也不太容易去掉,有兴趣的可以去了解下,Firebreath的源代码也可以作为基于NPAPI开发的一些参考,还有一个基于NPAPI做的简单的示例,结构非常简单,不用绕来绕去,相对理解起来也简单许多(npsimple

插件入门开发的示例 

可以参考的资料很多,这里删除了原文中的实例,相关的例子很多,此处仅提供参考地址:

http://hxis9e2q.i.sohu.com/blog/view/208654665.htm

http://www.cnblogs.com/chingliu/archive/2011/12/03/2288635.html

http://blog.csdn.net/z6482/article/details/7660748

分析np_entry.cppnpp_gate.cppnpn_gate.cpp三个文件

这三个文件中的函数非常重要。

 首先来看下np_entry.cpp中的函数

NP_GetEntryPoints函数,为插件入口的函数,插件初始化将会首先调用该函数

该函数用于初始化浏览器调用插件的函数表,NPP(np plugin)开头,

后面的插件的一些事件(new)发生时将会以这里初始化的函数作为入口,比如

 pFuncs->newp = NPP_New;初始化后将会在创建插件实例时调用NPP_New的实现来创建.

NP_Initialize函数,初始化插件时,NP_GetEntryPoints后调用,

该函数用于初始化插件调用浏览器的函数表,参数pFuncs带有该函数表信息,

我们自定义一个对象保存这些信息,今后就可通过该对象调用方法来实现对浏览器的一些操作

NP_Shutdown函数,NP_Initialize对应,主要释放资源等操作

 再来看下npp_gate.cpp

这个文件中的函数都以NPP开头,用于定义浏览器调用插件的方法

经过NP_GetEntryPoints的初始化后,当特定事件发生时,浏览器将会调用这些方法

然后需要注意的是该文件引用了plugin.h,是我们第5步创建的文件,名字不同可以改改

NPP_New方法,用于创建插件实例CPlugin * pPlugin = new CPlugin(instance);这句话为创建一个我们定义的CPlugin类对象,构造函数为NPP类型

NPP_Destroy方法,用于销毁插件实例,刷新页面,关闭页面等操作会触发该方法会调用CPluginshut方法再delete掉实例

NPP_SetWindow方法,插件窗口发生任何变化都会调用该方法window创建时会调用一次,如果初始化失败则delete掉实例然后返回错误

NPP_GetValue方法,当获取插件有关的一些信息时会触发该方法调用(如获取插件名,插件实例)

javascript操作插件对象时,该方法调用CPluginGetScriptableObject方法,需要自己实现,返回一个脚本操作对象(NPObject)。在这里返回到CPlugin,添加GetScriptableObject方法并实现(见第7步操作)

NPP_HandleEvent方法,处理事件,该方法调用CPluginhandleEvent方法,继续添加实现吧

该文件中其他方法暂时没什么可说的,需要用到的可以查下API并实现出来就行了.

再看下npn_gate.cpp

该文件实现了对浏览器的一些操作的函数,都以NPN(np netscape)开头其中有一些带有NPObject*参数的与GetScriptableObject方法创建的脚本操作对象有关,将在第7步做说明

该文件中用到的NPNetscapeFuncsNPNFuncs;

NP_Initialize中初始化完成

封装一个脚本操作对象

Add一个C++,该示例命名为PluginObject,继承NPObject

添加静态方法,用于创建该脚本操作的对象

public:static NPObject*_allocate(NPP npp,NPClass* aClass);static void _deallocate(NPObject*npobj);static void _invalidate(NPObject *npobj);static bool_hasMethod(NPObject* obj, NPIdentifier methodName);static bool_invokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount,NPVariant *result);static bool _invoke(NPObject* obj, NPIdentifier methodName,const NPVariant *args, uint32_t argCount, NPVariant *result);static bool_hasProperty(NPObject *obj, NPIdentifier propertyName);static bool _getProperty(NPObject*obj, NPIdentifier propertyName, NPVariant *result);static bool_setProperty(NPObject *npobj, NPIdentifier name,const NPVariant *value);staticbool _removeProperty(NPObject *npobj, NPIdentifier name);static bool_enumerate(NPObject *npobj, NPIdentifier **identifier,uint32_t *count);staticbool _construct(NPObject *npobj, const NPVariant *args,uint32_t argCount,NPVariant *result);
PluginObject.h中声明一个NPClass对象,使用上面的静态方法将该NPClass对象初始化

#ifndef__object_class#define __object_classstatic NPClass objectClass ={NP_CLASS_STRUCT_VERSION,PluginObject::_allocate,PluginObject::_deallocate,PluginObject::_invalidate,PluginObject::_hasMethod,PluginObject::_invoke,PluginObject::_invokeDefault,PluginObject::_hasProperty,PluginObject::_getProperty,PluginObject::_setProperty,PluginObject::_removeProperty,PluginObject::_enumerate,PluginObject::_construct};#endif回到第6步中在CPlugin类中实现的GetScriptableObject方法

在该方法中通过NPNCreateObject方法创建该对象

NPObject*CPlugin::GetScriptableObject(){return NPN_CreateObject(this->instance,&objectClass);}this->instance在构造函数时获取并保存下来的NPP对象.

NPN_CreateObject会在浏览器中做一些操作然后回来调用objectClass中的_allocate方法

需要实现该静态方法,new一个PluginObject

新建一个NPP npp属性,和一个NPP参数的构造函数

NPObject*PluginObject::_allocate(NPP npp,NPClass* aClass){return new PluginObject(npp);}

后面的操作中,浏览器调用了NPNFunc中以上的一些方法则会来调用这些静态方法,并将_allocate返回的值作为参数传到其他函数中

接下来的实现就相对比较随意了,可以直接在这些静态方法中实现想要的效果,

也可以在PluginObject中创建对应的成员函数实现,然后在静态方法中通过nobj参数转换为(PluginObject)类型调用相应成员函数

其中几个函数比较重要,_hasMethod判断参见是否有该函数,_getProperty则是判断属性,invoke调用相应方法,

invokeDefault可以在invoke中调用NPN_InvokeDefault来访问,最好不要直接调用,(API,原因未知,一般浏览器都要做进一步操作)

hasMethod等方法的类似于参数methodName都是以identifier作为判断的,可以调用NPN_GetStringIdentifier获取

例如:

PluginObject::PluginObject(NPPnpp){this->npp = npp;id_func_add =NPN_GetStringIdentifier("add");id_property_version =NPN_GetStringIdentifier("version");}boolPluginObject::hasMethod(NPObject* obj, NPIdentifiermethodName){if(methodName==this->id_func_add)return true;return false;}
多说下enumerate方法或者说是NPN_XXX的方法,因为就这个东西折腾我完完整整的两天时间...

enumerate方法的参数有个指针数组,但是他的结构是

而且初始化的时候一定要用NPN_MemAlloc来操作....API上只有关于指针数组的结构说明,而且很简单的提了一句,折腾两天才发现非得用NPN来分配内存- -||

boolPluginObject::enumerate(NPIdentifier **identifier,uint32_t *count){ *count = 1;NPIdentifier *outList(NULL);outList =(NPIdentifier*)NPN_MemAlloc((uint32_t)(sizeof(NPIdentifier) * *count));outList[0] = id_property_version; *identifier = outList; return true;}测试的时候在firebug的控制台输入plugin.就会去调用enumerate

注册及安装

1.注册表注册位置

HKEY_CURRENT_USER\Software\MozillaPlugins

添加一个项@whuiss.com/npTest

添加字符串值

"Description"="code projecttest"
"Path"="
pathto npTest.dll"
"ProductName"="npdemo Dynamic Library"
"Vendor"="zsy"
"Version"="1.0.0.1"

添加子项MIMETypes

添加MIMETypes的子项application/x-npTest

但是实际上只需要一个项@whuiss.com/npTest以及一个Path字符串值,其他可有可无

firefox地址栏输入about:plugins可查到你的插件了

2.使用安装文件注册

visual studio新建一个set up project

FileSystem View中选中dll或者某个工程的输出

Registry View中按照上面的位置给添加上相应信息即可

使用插件

<html><head><script>window.onready= function(){}function toDoSt(){var plugin =document.getElementById("plugin");alert(plugin.version);}</script><embedid="plugin" type="application/x-npTest" src="file:///pathto npTest.dll" pluginspage="http://xxxx"></head><body><inputtype="button" onclick="toDoSt()"value="test"></body></html>其中embedsrcpluginspage可有可无

调试插件

先前一直弄错了,以为是指向Firefox.exe,查了好久,发现原来在Firefox4之后新建了一个plugin-container.exe进程

调试目标指向plugin-container.exe或者tools->attach to process选中plugin-container.exe进程或者debug->attachto process

参考:http://blog.csdn.net/z6482/article/details/7664789