NPAPI插件开发详细记要:插件运行流程分析

NPAPI插件开发详细记录:插件运行流程分析
本文详细分析插件的代码是如何执行的,主要分析np_entry.cpp、npn_gate.cpp和npp_gate.cpp.希望能够有所收获。
在windows平台下,插件就是一个dll,注意到这个dll的def文件内容是:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. LIBRARY ""  
  2.   
  3. EXPORTS  
  4. NP_GetEntryPoints @1  
  5. NP_Initialize @2  
  6. NP_Shutdown @3  

插件接口

既然是浏览器调用插件,必然浏览器是通过上面三个接口来调用的。上述三个接口,第三个很明显是结束插件时调用。
参考资料:http://colonelpanic.net/2009/03/building-a-firefox-plugin-part-one/
NP_GetEntryPoints – 在插件加载之后立即调用该接口,用于浏览器获取所有可能需要调用的API函数的指针。
NP_Initialize – 为插件提供全局初始化。
NP_Shutdown – 为插件提供全局反初始化。
在np_entry.cpp文件中可以找到上面的几个函数。第一个:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs)  
  2. {  
  3. return fillPluginFunctionTable(aNPPFuncs);  
  4. }  

其调用的fillPluginFunctionTable为:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs)  
  2. {  
  3. if (!aNPPFuncs)  
  4. return NPERR_INVALID_FUNCTABLE_ERROR;  
  5.   
  6. // Set up the plugin function table that Netscape will use to call us.  
  7. aNPPFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;  
  8. aNPPFuncs->newp = NPP_New;  
  9. aNPPFuncs->destroy = NPP_Destroy;  
  10. aNPPFuncs->setwindow = NPP_SetWindow;  
  11. aNPPFuncs->newstream = NPP_NewStream;  
  12. aNPPFuncs->destroystream = NPP_DestroyStream;  
  13. aNPPFuncs->asfile = NPP_StreamAsFile;  
  14. aNPPFuncs->writeready = NPP_WriteReady;  
  15. aNPPFuncs->write = NPP_Write;  
  16. aNPPFuncs->print = NPP_Print;  
  17. aNPPFuncs->event = NPP_HandleEvent;  
  18. aNPPFuncs->urlnotify = NPP_URLNotify;  
  19. aNPPFuncs->getvalue = NPP_GetValue;  
  20. aNPPFuncs->setvalue = NPP_SetValue;  
  21.   
  22. return NPERR_NO_ERROR;  
  23. }  

fillPluginFunctionTable函数设置了一系列的函数入口,都是很熟悉的在mozilla的开发文档中经常提到的以NPP开头的函数。这些函数是需要在插件中加以实现的。

第二个:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs)  
  2. {  
  3. NPError rv = fillNetscapeFunctionTable(aNPNFuncs);  
  4. if (rv != NPERR_NO_ERROR)  
  5. return rv;  
  6.   
  7. return NS_PluginInitialize();  
  8. }  

其调用的fillNetscapeFunctionTable为:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs)  
  2. {  
  3. if (!aNPNFuncs)  
  4. return NPERR_INVALID_FUNCTABLE_ERROR;  
  5.   
  6. if (HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR)  
  7. return NPERR_INCOMPATIBLE_VERSION_ERROR;  
  8.   
  9. if (aNPNFuncs->size < sizeof(NPNetscapeFuncs))  
  10. return NPERR_INVALID_FUNCTABLE_ERROR;  
  11.   
  12. NPNFuncs.size = aNPNFuncs->size;  
  13. NPNFuncs.version = aNPNFuncs->version;  
  14. NPNFuncs.geturlnotify = aNPNFuncs->geturlnotify;  
  15. NPNFuncs.geturl = aNPNFuncs->geturl;  
  16. NPNFuncs.posturlnotify = aNPNFuncs->posturlnotify;  
  17. NPNFuncs.posturl = aNPNFuncs->posturl;  
  18. NPNFuncs.requestread = aNPNFuncs->requestread;  
  19. NPNFuncs.newstream = aNPNFuncs->newstream;  
  20. NPNFuncs.write = aNPNFuncs->write;  
  21. NPNFuncs.destroystream = aNPNFuncs->destroystream;  
  22. NPNFuncs.status = aNPNFuncs->status;  
  23. NPNFuncs.uagent = aNPNFuncs->uagent;  
  24. NPNFuncs.memalloc = aNPNFuncs->memalloc;  
  25. NPNFuncs.memfree = aNPNFuncs->memfree;  
  26. NPNFuncs.memflush = aNPNFuncs->memflush;  
  27. NPNFuncs.reloadplugins = aNPNFuncs->reloadplugins;  
  28. NPNFuncs.getvalue = aNPNFuncs->getvalue;  
  29. NPNFuncs.setvalue = aNPNFuncs->setvalue;  
  30. NPNFuncs.invalidaterect = aNPNFuncs->invalidaterect;  
  31. NPNFuncs.invalidateregion = aNPNFuncs->invalidateregion;  
  32. NPNFuncs.forceredraw = aNPNFuncs->forceredraw;  
  33.   
  34. return NPERR_NO_ERROR;  
  35. }  

这里获取一系列函数的入口,这些函数是浏览器中实现的。

NP_Initialize还调用了一个函数NS_PluginInitialize(),NS_PluginInitialize是我们在plugin.cpp中实现的,随便提一句,NP_Shutdown调用的NS_PluginShutdown也是在plugin.cpp中由我们自己去实现的。
通过对上面的代码的分析,可以发现虽然在def里面只定义了三个接口,但实际上却包括浏览器实现的由浏览器调用的接口21个以及浏览器要调用的由插件实现的接口13个(实际上NPNetscapeFuncs结构定义了55个函数指针,NPPluginFuncs结构定义了19个函数指针)。换句话说,我们开发插件就是要来实现这13个接口。接口的标准已经由浏览器定义好了,我们怎么去实现以及要实现什么样的功能就全凭我们自己了。
np_entry.cpp这个文件已经分析得差不多了,这个文件包含了两个头文件,"npplat.h"和"pluginbase.h",打开这两个文件来看看里面定义了些什么。npplat.h很简单:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #ifndef npplat_h_  
  2. #define npplat_h_  
  3.   
  4. #include "npapi.h"  
  5. #include "npfunctions.h"  
  6.   
  7. #ifdef XP_WIN  
  8. #include "windows.h"  
  9. #endif  
  10.   
  11. #ifdef XP_UNIX  
  12. #include <stdio.h>  
  13. #endif  
  14.   
  15. #ifdef XP_MAC  
  16. #include <Carbon/Carbon.h>  
  17. #endif  
  18.   
  19. #ifndef HIBYTE  
  20. #define HIBYTE(i) (i >> 8)  
  21. #endif  
  22.   
  23. #ifndef LOBYTE  
  24. #define LOBYTE(i) (i & 0xff)  
  25. #endif  
  26.   
  27. #endif  

另外看看pluginbase.h,该文件定义了nsPluginInstanceBase类并声明了四个全局函数: NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)

NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
NS_PluginInitialize();
NS_PluginShutdown();
这四个函数都是由我们在plugin.cpp中去实现的,前面的分析中已经知道NS_PluginInitialize()和NS_PluginShutdown()是在np_entry.cpp文件中调用的。
由此观之,我们要开发插件,建立的类首先要继承nsPluginInstanceBase类根据需要实现其中的某些虚函数,另外还需要实现上面这四个全局函数。
接下来研究npn_gate.cpp文件和npp_gate.cpp文件。

NPN函数

打开npn_gate.cpp文件,就可以发现其中实现了20个函数,都是fillNetscapeFunctionTable中的函数,(之前我们发现其中有21个函数,这里为什么只有20个呢?看看NPNetscapeFuncs的定义可以发现,size是个变量不是函数。)而这里的函数名就是以NPN_开头了,按说这是浏览器实现函数不需要在这些地方来实现了呀,这是什么原因呢。来看看其中的函数吧。
以一个简单的函数NPN_GetURL为例:其实现代码如下:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. NPError NPN_GetURL(NPP instance, const char *url, const char *target)  
  2. {  
  3. return (*NPNFuncs.geturl)(instance, url, target);  
  4. }  

这个一看咱就明白了,NPN_GetURL确实可以说是浏览器实现的,因为这个函数直接调用了NPNetscapeFuncs的一个函数地址处的函数。

换句话说,在插件实例初始化的时候,将浏览器实现的这些函数的入口地址保存到一个NPNetscapeFuncs结构中,NPN_开头的这些函数名其实是开发插件时需要由开发者定义的,这些函数的实现就直接根据NPNetscapeFuncs结构中的入口地址调用浏览器实现的相关功能。但这些基本都是固定不变的,因此sdk中已经帮我们开发者写好了这些代码,在开发插件时只需要调用NPN_开头的全局函数即可。

NPP函数

npp_gate.cpp文件中实现了13个函数,这13个函数就是NP_GetEntryPoints中fillPluginFunctionTable需要实现的函数。
在npp_gate.cpp文件中我们可以发现分别在NPP_New和NPP_Destroy中调用了plugin.cpp中实现的另外两个全局函数NS_NewPluginInstance和NS_DestroyPluginInstance。
这个文件中实现的13个函数基本上都调用了nsPluginInstanceBase类对应的函数,因此这些NPP_开头的函数就相当于是插件开发者实现的。实现这些函数就是要实现nsPluginInstanceBase类的成员函数,可以看到nsPluginInstanceBase的成员函数都是定义为虚函数的,其中 init(NPWindow* aWindow) 、shut()、 isInitialized()三个函数是纯虚函数,在nsPluginInstanceBase类的派生类中必须进行实现,而其他函数就可以根据需要加以实现。
通过上面的一番分析,要写出一个NPAPI的插件,利用这个框架必须至少要做的工作有:
从nsPluginInstanceBase类派生一个类并至少实现其中init() 、shut()、 isInitialized()这三个成员函数。
声明并实现四个全局函数

nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct);
void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
NPError NS_PluginInitialize();
void NS_PluginShutdown();
到这里就对插件的接口分析完毕了,开发插件的过程就是实现nsPluginInstanceBase类的部分成员函数以实现需要的功能。要想得心应手的开发插件就必须准确的把握浏览器调用这些NPP函数的机制和流程。

浏览器对插件接口的调用

接下来分析浏览器在何时调用何种接口的问题。
首先来理一下nsPluginInstanceBase类的成员函数是如何被NPP_开头的函数所调用的。(注:plugin表示一个nsPluginInstanceBase对象)

NPP接口函数

可能调用的nsPluginInstanceBase成员函数或全局函数

备注

NPP_New

NS_NewPluginInstance

创建插件实例

NPP_Destroy

NS_DestroyPluginInstance、plugin->shut

删除插件实例

NPP_SetWindow

plugin->SetWindow、plugin->isInitialized、plugin->init、NS_DestroyPluginInstance

窗口创建、移动、改变大小或销毁时调用

NPP_NewStream

plugin->NewStream

通知插件实例有新的数据流

NPP_WriteReady

plugin->WriteReady

确定插件是否准备好接收数据(以及其准备接收的最大字节数)

NPP_Write

plugin->Write

调用以将数据读入插件this might be better named “NPP_DataArrived”

NPP_DestroyStream

plugin->DestroyStream

通知插件实例数据流将要关闭或销毁

NPP_StreamAsFile

plugin->StreamAsFile

为创建流数据提供本地文件名

NPP_Print

plugin->Print

为嵌入或全屏插件请求平台特定的打印操作

NPP_URLNotify

plugin->URLNotify

通知插件已完成URL请求

NPP_GetValue

plugin->GetValue

调用以查询插件信息(还用来获取NPObject/Scriptable 插件的实例)

NPP_SetValue

plugin->SetValue

这是用来为浏览器提供插件变量信息的

NPP_HandleEvent

plugin->HandleEvent

事件处理函数,对windowed的插件只在MAC操作系统上可用,对于winless的插件所有平台都可用

可见,NPP接口基本上与nsPluginInstanceBase类的成员函数一一对应。
下面这段文字译自:http://colonelpanic.net/2009/05/building-a-firefox-plugin-part-two/

当你明确了插件的生命周期之后会发现它事实上非常简单。初始化入口 NP_Initialize和NP_GetEntryPoints的调用顺序不确定(根据相关文档);但是在实际中,Windows平台上貌似NP_GetEntryPoints 先被调用。记住这一点,下面就是Windows平台上基本的windowed插件初始化的调用顺序:
1. NP_GetEntryPoints – 插件用NPP_New, NPP_Destroy, NPP_SetWindow等函数的入口地址填充一个函数表。
2. NP_Initialize – 插件存储一个NPN_CreateObject, NPN_MemAlloc,等函数入口地址组成的函数表的拷贝。
3. NPP_New – 插件创建一个新的插件实例并初始化
4. NPP_SetWindow – 每个实例都会多次调用这个函数——每次实例窗口创建、改变大小或者其他变化都会调用。
5. NPP_GetValue (Variable = NPPVpluginScriptableNPObject) – 插件创建一个支持脚本的NPObject并返回其指针(调用NPN_RetainObject)。
6. — 标准的插件活动 —
7. NPP_Destroy – 销毁插件实例
8. NP_Shutdown – 销毁所有遗留的插件资源
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值