一.Netscape Plugin Interface(NPAPI)

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

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

与javascript的交互需要用到NPAPI中的npruntime Scripting plugins

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

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

下载后在\firefox-4.0.1.source\mozilla-2.0\modules\plugin可以找到一些samples和头文件

这里为方便下载,上传了一份单独的plugin文件夹

另外,基于NPAPI的一个跨浏览器插件开发的框架FireBreath,非常容易上手而且据说跨浏览器的支持非常好,但是非常笨重,有些功能不需要的也不太容易去掉

Firebreath,有兴趣的可以去了解下,Firebreath的源代码也可以作为基于NPAPI开发的一些参考

还有一个基于NPAPI做的简单的示例,结构非常简单,不用绕来绕去,相对理解起来也简单许多

npsimple

二.插件入门开发的示例

开发工具为visual studio 2010

1.新建一个Win32 project,命名以np开头(目的是编译完的Dll名必须以np开头才能被识别为插件)

类型为一个DLL的空工程即可

2.右键选中项目的属性,在VC++ Directories目录下,选择Include Directories,Edit,

将plugin/base/public和plugin/sdk/samples/include添加到include

3.新建Version资源文件

  1. // Microsoft Visual C++ generated resource script.
  2. //
  3. #include "resource.h"
  4. #define APSTUDIO_READONLY_SYMBOLS
  5. /////////////////////////////////////////////////////////////////////////////
  6. //
  7. // Generated from the TEXTINCLUDE 2 resource.
  8. //
  9. #include "afxres.h"
  10. /////////////////////////////////////////////////////////////////////////////
  11. #undef APSTUDIO_READONLY_SYMBOLS
  12. /////////////////////////////////////////////////////////////////////////////
  13. // Chinese (Simplified, PRC) resources
  14. #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
  15. LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
  16. #ifdef APSTUDIO_INVOKED
  17. /////////////////////////////////////////////////////////////////////////////
  18. //
  19. // TEXTINCLUDE
  20. //
  21. 1 TEXTINCLUDE
  22. BEGIN
  23. "resource.h\0"
  24. END
  25. 2 TEXTINCLUDE
  26. BEGIN
  27. "#include ""afxres.h""\r\n"
  28. "\0"
  29. END
  30. 3 TEXTINCLUDE
  31. BEGIN
  32. "\r\n"
  33. "\0"
  34. END
  35. #endif    // APSTUDIO_INVOKED
  36. /////////////////////////////////////////////////////////////////////////////
  37. //
  38. // Version
  39. //
  40. VS_VERSION_INFO VERSIONINFO
  41. FILEVERSION 1,0,0,1
  42. PRODUCTVERSION 1,0,0,1
  43. FILEFLAGSMASK 0x3fL
  44. #ifdef _DEBUG
  45. FILEFLAGS 0x1L
  46. #else
  47. FILEFLAGS 0x0L
  48. #endif
  49. FILEOS 0x40004L
  50. FILETYPE 0x2L
  51. FILESUBTYPE 0x0L
  52. BEGIN
  53. BLOCK "StringFileInfo"
  54. BEGIN
  55. BLOCK "040904e4"
  56. BEGIN
  57. VALUE "CompanyName", "WHU ISS"
  58. VALUE "FileDescription", "A new Plugin For test"
  59. VALUE "FileVersion", "1.0.0.1"
  60. VALUE "InternalName", "npTest.dll"
  61. VALUE "LegalCopyright", "Copyright (C) 2012"
  62. VALUE "MIMEType", "application/x-npTest"
  63. VALUE "OriginalFilename", "npTest.dll"
  64. VALUE "ProductName", "new Plugin Test"
  65. VALUE "ProductVersion", "1.0.0.1"
  66. END
  67. END
  68. BLOCK "VarFileInfo"
  69. BEGIN
  70. VALUE "Translation", 0x804, 1200
  71. END
  72. END
  73. #endif    // Chinese (Simplified, PRC) resources
  74. /////////////////////////////////////////////////////////////////////////////
  75. #ifndef APSTUDIO_INVOKED
  76. /////////////////////////////////////////////////////////////////////////////
  77. //
  78. // Generated from the TEXTINCLUDE 3 resource.
  79. //
  80. /////////////////////////////////////////////////////////////////////////////
  81. #endif    // not APSTUDIO_INVOKED

需要注意的是Block 必须为040904e4,MIMEType为最后引用插件的标志

4.新建一个Module-Definition File(.def),定义入口函数

  1. LIBRARY   npTest
  2. EXPORTS
  3. NP_GetEntryPoints   @1
  4. NP_Initialize       @2
  5. NP_Shutdown         @3

5.新建一个CPlugin类继承nsPluginInstanceBase,作为插件实例类(后面再说该类的作用)

确定之后,在plugin.h中#include <pluginbase.h>

类名为Cplugin,头文件名为plugin.h,(npp_gate.cpp会使用到,不同可以修改)

修改构造函数的实现,带参数NPP类型并新建一个属性保存该参数

实现父类的三个纯虚函数

  1. NPBool init(NPWindow* aWindow);//NPWindow用于插件中绘画部件的窗口
  2. void shut();
  3. NPBool isInitialized();

6.免得做过多操作,从samples中引入已经编写好的入口函数

从plugin\sdk\samples\npruntime路径添加np_entry.cpp(插件入口函数),npn_gate.cpp(插件调用浏览器的一些方法),npp_gate.cpp(浏览器调用插件的一些方法)

添加后需要做一点修改,

1).np_entry.cpp和npn_gate.cpp的引用

#include "npapi.h"
#include "npfunctions.h"

换成

#include<pluginbase.h>

2).然后进入pluginbase.h,再进入npplat.h,将

#ifdef XP_WIN
#include "windows.h"
#endif

挪到

#include "npapi.h"
#include "npfunctions.h"

前面,

3).然后在项目属性,Preprocessor,Preprocessor Definitions添加XP_WIN的定义

(这样做的原因是windows.h需要在npapi.h前定义,自己在所有引用了npapi.h的前面加上windows.h的引用也可以)

4),np_entry.cpp中引入头文件#include <stddef.h>

因为使用到offsetof

这三个文件中的函数非常重要,首先来看下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方法,用于销毁插件实例,刷新页面,关闭页面等操作会触发

该方法会调用CPlugin的shut方法再delete掉实例

NPP_SetWindow方法,插件窗口发生任何变化都会调用该方法

window创建时会调用一次,如果初始化失败则delete掉实例然后返回错误

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

当javascript操作插件对象时,该方法调用CPlugin的GetScriptableObject方法,需要自己实现,返回一个脚本操作对象(NPObject)

在这里返回到CPlugin类,添加GetScriptableObject方法并实现(见第7步操作)

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

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

再看下npn_gate.cpp,该文件实现了对浏览器的一些操作的函数,都以NPN(np netscape)开头

其中有一些带有NPObject*参数的与GetScriptableObject方法创建的脚本操作对象有关,将在第7步做说明

该文件中用到的NPNetscapeFuncs NPNFuncs;在NP_Initialize中初始化完成

7.封装一个脚本操作对象

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

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

  1. public:
  2. static NPObject* _allocate(NPP npp,NPClass* aClass);
  3. static void _deallocate(NPObject *npobj);
  4. static void _invalidate(NPObject *npobj);
  5. static bool _hasMethod(NPObject* obj, NPIdentifier methodName);
  6. static bool _invokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
  7. static bool _invoke(NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
  8. static bool _hasProperty(NPObject *obj, NPIdentifier propertyName);
  9. static bool _getProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result);
  10. static bool _setProperty(NPObject *npobj, NPIdentifier name,const NPVariant *value);
  11. static bool _removeProperty(NPObject *npobj, NPIdentifier name);
  12. static bool _enumerate(NPObject *npobj, NPIdentifier **identifier,uint32_t *count);
  13. static bool _construct(NPObject *npobj, const NPVariant *args,uint32_t argCount, NPVariant *result);

在PluginObject.h中声明一个NPClass对象,使用上面的静态方法将该NPClass对象初始化

  1. #ifndef __object_class
  2. #define __object_class
  3. static NPClass objectClass = {
  4. NP_CLASS_STRUCT_VERSION,
  5. PluginObject::_allocate,
  6. PluginObject::_deallocate,
  7. PluginObject::_invalidate,
  8. PluginObject::_hasMethod,
  9. PluginObject::_invoke,
  10. PluginObject::_invokeDefault,
  11. PluginObject::_hasProperty,
  12. PluginObject::_getProperty,
  13. PluginObject::_setProperty,
  14. PluginObject::_removeProperty,
  15. PluginObject::_enumerate,
  16. PluginObject::_construct
  17. };
  18. #endif

回到第6步中在CPlugin类中实现的GetScriptableObject方法

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

  1. NPObject* CPlugin::GetScriptableObject(){
  2. return NPN_CreateObject(this->instance,&objectClass);
  3. }

this->instance在构造函数时获取并保存下来的NPP对象.

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

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

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

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

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

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

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

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

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

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

例如:

  1. PluginObject::PluginObject(NPP npp)
  2. {
  3. this->npp = npp;
  4. id_func_add = NPN_GetStringIdentifier("add");
  5. id_property_version = NPN_GetStringIdentifier("version");
  6. }
  7. bool PluginObject::hasMethod(NPObject* obj, NPIdentifier methodName)
  8. {
  9. if(methodName==this->id_func_add)
  10. return true;
  11. return false;
  12. }

多说下enumerate方法或者说是NPN_XXX的方法,因为就这个东西折腾我完完整整的两天时间...

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

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

弱弱的总结下,应该是需要给Firefox用到的东西或者说从参数传进来需要你分配内存的都得用NPN_MemAlloc分配内存

如果出现Access Violation,首先想到什么地方应该用NPN_MemAlloc....

  1. bool PluginObject::enumerate(NPIdentifier **identifier,uint32_t *count)
  2. {
  3. *count = 1;
  4. NPIdentifier *outList(NULL);
  5. outList = (NPIdentifier*)NPN_MemAlloc((uint32_t)(sizeof(NPIdentifier) * *count));
  6. outList[0] = id_property_version;
  7. *identifier = outList;
  8. return true;
  9. }

测试的时候在firebug的控制台输入 plugin. 就会去调用enumerate了

三.注册及安装

1.注册表注册位置

HKEY_CURRENT_USER\Software\MozillaPlugins

添加一个项@whuiss.com/npTest

添加字符串值

"Description"="code project test"
"Path"="path to 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中按照上面的位置给添加上相应信息即可

四.使用插件

  1. <html>
  2. <head>
  3. <script>
  4. window.onready = function(){
  5. }
  6. function toDoSt(){
  7. var plugin = document.getElementById("plugin");
  8. alert(plugin.version);
  9. }
  10. </script>
  11. <embed id="plugin" type="application/x-npTest" src="file:///path to npTest.dll" pluginspage="http://xxxx">
  12. </head>
  13. <body>
  14. <input type="button" onclick="toDoSt()" value="test">
  15. </body>
  16. </html>

其中embed的src和pluginspage可有可无

五.调试插件

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

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

六.附上示例工程

npTest工程下载地址

打开工程后需要修改include directory

------------------------------------------------------分割线-----------------------------------------------------

发现个新问题,NPAPI执行函数返回值不支持带中文的么?

调试很多次了,也不知道是配置问题还是什么问题,NPVariant *result中带有值返回的

但是到浏览器就变成空字符串,去掉中文的就能正常显示

Firebreath的也试过了,也不支持中文字符

没办法,只好将返回的值转成base64再在浏览器解码,这样倒是可以正常

------------------------------------------------------分割线-----------------------------------------------------

firefox新版本 弹出winform(例如访问某些智能卡私钥会需要输入PIN)的时候导致假死的情况,在火狐社区提问了,能够解决

http://mozilla.com.cn/post/31422/#reply-24747

大致意思就是修改config里面的dom.ipc.plugins.enabled.your-plugin.dll=false

from:http://blog.csdn.net/hzzhoushaoyu/article/details/7387516

NPAPI——实现非IE浏览器的类似ActiveX的本地程序(插件)调用的更多相关文章

  1. 非IE内核浏览器如何支持activex插件

    原文地址:https://blog.csdn.net/johnson2008t/article/details/46126605 之前在一个B/S项目中遇到一个需求,就是客户需要在页面上对报表的布局以 ...

  2. ASP.NET MVC 使用Jquery Uploadify 在非IE浏览器下Http Error的解决方案

    解决Uploadify上传控件在非IE浏览器中不工作,需要做如下2步修改: 1.Global.asax文件中,实现Application_BeginRequest函数: void Applicatio ...

  3. VS2010在非IE浏览器下调试Silverlight程序

    以Chrome为例: 第一步:在程序中设置断点. 第二步:右键点击web应用程序的起始页(.html或.aspx文件),选择"浏览方式",选中Chrome或其它非IE浏览器,点&q ...

  4. CSS hack 如何区分所有IE浏览器和非IE浏览器

    网上方法很多,例如,测试后得出以下结论,多余的话不说了,直入主题: 1.所有的推理IE浏览器 正解:此写法仅仅被lE浏览器识别,非IE浏览器不识别. <!--[if IE]> <st ...

  5. 黄聪:微信h5支付demo微信H5支付demo非微信浏览器支付demo微信wap支付

    一.首先先确定H5支付权限已经申请! 二.开发流程 1.用户在商户侧完成下单,使用微信支付进行支付 2.由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB ...

  6. 兼容IE9以下和非IE浏览器的原生js事件绑定函数

    事件绑定函数的demo如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "htt ...

  7. H5版如何在微信外(非微信浏览器)进行微信支付技术方案

    官方是支持在非微信内置浏览器中调起微信支付的!H5支付是基于公众号基础开发的一种非微信内浏览器支付方式(需要单独申请支付权限),可以满足在微信外的手机H5页面进行微信支付的需求.同时,由于H5链接传播 ...

  8. (转)如何让ActiveXObject( "Microsoft.XmlDom ")对象在非IE浏览器下显示数据?firefox(火狐)

    如何让ActiveXObject( "Microsoft.XmlDom ")对象在非IE浏览器下显示数据?firefox(火狐) 2013-09-10 16:01 2152人阅读 ...

  9. 非ie浏览器必备函数常识

    场景描述: 我们都知道IE浏览器和非IE浏览器都有很多功能一样但写法不同,或者各自都有一些自己独特的方法,那么为了保持兼容性和便于编写,我们可以通过这两个方法给非IE浏览器的对象增加自己没有,但IE有 ...

随机推荐

  1. MVC学习日记(三)EntityFramework

    其实学会了第一篇的创建和第二篇的使用以后,基本的mvc操作足够了,至于验证神马的,还不如用Jquery.h5的好看适用,所以接下来, 后续上会讲一些比较实用的. 在之前的文章说到了基础的使用, 那么, ...

  2. P1304 哥德巴赫猜想

    题目描述 输入N(N<=10000),验证4~N所有偶数是否符合哥德巴赫猜想. (N为偶数). 如果一个数,例如10,则输出第一个加数相比其他解法最小的方案.如10=3+7=5+5,则10=5+ ...

  3. 如何修改 WordPress 数据库前缀

    我们知道 WordPress 的数据库表,可以设置前缀,默认是 wp_,很多同学也就默认用了 wp_,如果某种原因(比如提高安全性)要修改的 WordPress 数据的前缀,我们应该怎么做? 开始之前 ...

  4. javascript中天气接口案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 开源作品-PHP写的在线文件管理工具(单文件绿色版)-SuExplorer_PHP_3_0

    前言:项目开发过程中,网站一般部署到远程服务器,所以文件管理就不能和本机操作一样方便.通常文件管理是用ftp下载到本地,修改后再上传,或者远程登录到服务器进行修改.但是这些操作都依赖于复杂的第三方软件 ...

  6. UWP Ad

    1.对于 UWP 应用:使用 Visual Studio 2015 安装 Microsoft Store Services SDK 2.对于通用 Windows 平台 (UWP) 项目:展开通用 Wi ...

  7. span可编辑 属性 html 可编辑td

    <span contenteditable="true">11111111111111111</span> <!DOCTYPE html PUBLIC ...

  8. 查看Linux系统版本信息的几种方法

    一.查看Linux内核版本命令(两种方法): 1.cat /proc/version 2.uname -a 二.查看Linux系统版本的命令(3种方法): 1.lsb_release -a,即可列出所 ...

  9. Ikki's Story IV - Panda's Trick POJ - 3207_dfs跑2-SAT

    Code: #include<cstdio> #include<algorithm> #include<vector> using namespace std; c ...

  10. 路飞学城Python-Day171

    Evernote Export 线性结构: python的列表操作 列表是如何存储的:顺序存储的,是一块连续的内存,内存是一堆格子,列表是一串连续的编号 32位机器上一个整数占4个字节 数组和列表有2 ...