一.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. unity3d 让物体移动到点击位置

    using UnityEngine; using System.Collections; public class test : MonoBehaviour { //在场景中鼠标点击地面后,角色可以移 ...

  2. hiho一下 第174周

    题目1 : Dice Possibility 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 What is possibility of rolling N dice ...

  3. <a>和<table>标签的应用

    今天介绍一下html中最重要的标签 标签分为 1.一般标签 如<img> <b></b>等 2.功能标签 如<a></a> 3.实体 如&a ...

  4. 使用JDK和axis2发布webservice

    最近使用webservice进行远程调用一直很火,自从JDK1.6版本发布后,发布一个webservice项目变得更加简单了 笔者由于工作的需要针对JDK和axis2如何发布webservice做过相 ...

  5. Centos7下git服务器及gogs部署

    1.安装git # yum install -y git 2.创建git用户及组 # groupadd git # adduser git -g git # mkdir /home/git # mkd ...

  6. 3D集合图元:最小边界框/包围盒(boundingbox)

    对于2D边界框的应用时比较广泛地,它为一个简单匹配建立了很小的计算规则,3D模型的boundingbox则比较困难,计算代价较大.对于PCL库的使用则降低了计算难度,三维数值化降低了建模过程,可以使用 ...

  7. Integer Intervals POJ - 1716_查分约束_

    Code: #include<cstdio> #include<queue> #include<algorithm> using namespace std; co ...

  8. 洛谷P1231 教辅的组成 最大流

    裸题… Code: #include<cstdio> #include<cstring> #include<algorithm> #include<vecto ...

  9. 训练1-H

    小明今年3岁了, 现在他已经能够认识100以内的非负整数, 并且能够进行100以内的非负整数的加法计算. 对于大于等于100的整数, 小明仅保留该数的最后两位进行计算, 如果计算结果大于等于100, ...

  10. redis_2 数据类型

    1.key Redis keys 命令 下表给出了与 Redis 键相关的基本命令: 序号 命令及描述 1 DEL key该命令用于在 key 存在时删除 key. 2 DUMP key 序列化给定 ...