NPAPI——实现非IE浏览器的类似ActiveX的本地程序(插件)调用

 

一.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资源文件

 

[plain] view plain copy

 
 print?

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

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

 

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

 

[plain] view plain copy

 

 print?

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

 

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

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

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

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

实现父类的三个纯虚函数

 

[cpp] view plain copy

 

 print?

  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

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

 

[cpp] view plain copy

 

 print?

  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对象初始化

 

[cpp] view plain copy

 

 print?

  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方法创建该对象

 

[cpp] view plain copy

 

 print?

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

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

 

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

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

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

 

[cpp] view plain copy

 

 print?

  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获取

例如:

 

[cpp] view plain copy

 

 print?

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

 

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

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

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

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

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

 

[cpp] view plain copy

 

 print?

  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.   
  10. }  

测试的时候在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中按照上面的位置给添加上相应信息即可

 

四.使用插件

 

[html] view plain copy

 

 print?

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

时间: 2024-10-30 18:37:48

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

非IE浏览器实现onmouseenter

每次遇到onmouseover事件的处理就头疼.比如父标签A里面嵌套了两个子标签,分别为标签A1和A2.假设你在父标签A上绑定了mouseover事件,理想的状态应该是:当鼠标从A标签外移到A标签上,事件触发. 但实际情况是,在以下几种情况下,mouseover事件都会被触发,下图为mouseover事件触发示意图(mouseout事件类似). 当鼠标从A标签外移到A标签上. 当鼠标从父标签A移入子标签B(或C). 当鼠标从子标签B移入子标签C(假设间距足够小的话). 之前一直是通过在事件处理函

初学者的忧伤-我想在非ie浏览器的页面中执行一个js,然后打开一个指定的ie页面

问题描述 我想在非ie浏览器的页面中执行一个js,然后打开一个指定的ie页面 ActiveX只有在ie中才能运行,我现在想在谷歌或者火狐浏览器网页中自动打开一个ie浏览器的指定页面,请问直接用js可以实现吗? 解决方案 [原创]在winform程序中实现在IE浏览器中打开一个新的页面,全屏化并屏蔽IE窗口的工具栏和地址栏 解决方案二: 没有这种办法.页面在哪个浏览器打开就会在哪个浏览器呈现,页面不属于任何一个浏览器专有.你要想支持activeX,就只能在IE浏览器中打开.

ajax如何处理火狐浏览器或其它非ie浏览器的兼容性

如果选择的浏览器不是 Internet Explorer,或者为非 Microsoft 浏览器编写代码,就需要使用不同的代码.事实上就是 清单 1 所示的一行简单代码: var xmlHttp = new XMLHttpRequest object;. 这行简单得多的代码在 Mozilla.Firefox.Safari.Opera 以及基本上所有以任何形式或方式支持 Ajax 的非 Microsoft 浏览器中,创建了XMLHttpRequest对象. 结合起来 关键是要支持所有浏览器.谁愿意编

XHTML:非IE浏览器里对style.height赋值无效的问题

xhtml|浏览器|问题 今天我在做多页面MDI脚本控件(MzMultiPage),在IE等浏览器里都已经测试通过没有任何问题,但是在Mozilla.Firefox.Netscape里测试时问题就来了,我的多页面是在页面里用到了 iframe,这些 iframe 初始的 style.height 都是0,而在加载页面之后,iframe 就会自适应被加载的页面高度,我是通过 iframe.style.height=300 这样撑起 iframe 的高度,这样处理在IE系列浏览器里没有任何问题,但在

IE 和非IE浏览器的条件注释

浏览器|条件 (X)HTML 下面一段代码是测试在微软的IE浏览器下的条件注释语句的效果 <!--[if IE]><h1>您正在使用IE浏览器</h1><![endif]--><!--[if IE 5]><h1>版本 5</h1><![endif]--><!--[if IE 5.0]><h1>版本 5.0</h1><![endif]--><!--[if IE

如何在IE浏览器中屏蔽ActiveX控件

  ActiveX控件广泛应用于Internet,它们可以通过提供视频.动画内容等来增加浏览的乐趣.不过,这些程序可能出问题或者向你提供不需要的内容.在某些情况下,这些程序可被用来以你不允许的方式从计算机收集信息.破坏计算机上的数据.在未经同意的情况下在计算机上安装软件或者允许他人远程控制计算机.因此,有时要屏蔽掉这些对计算机安全构成威胁的ActiveX控件. 第1步:打开深度xp系统下载的IE浏览器窗口,在菜单栏单击"工具"-"Internet选项"命令,然后在打

html-如何在非ie浏览器使用js打开文件,并获取文件内容

问题描述 如何在非ie浏览器使用js打开文件,并获取文件内容 如何在非ie浏览器使用js打开文件,并获取文件内容? html中是一个input 解决方案 用ajax的方式 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <script type="text/javascript" src=

activex-linux环境 java 开发类似Activex功能的插件或控件

问题描述 linux环境 java 开发类似Activex功能的插件或控件 linux环境 java 开发类似Activex功能的插件或控件,能够访问本地的程序

求资源 求分享-自定义弹出确定取消框,方便程度最好和confirm一样。主要是在非ie浏览器上兼容

问题描述 自定义弹出确定取消框,方便程度最好和confirm一样.主要是在非ie浏览器上兼容 自定义弹出确定取消框,方便程度最好和confirm一样.主要是在非ie浏览器上兼容.求源码.用js或者css实现都可以. 解决方案 artDialog,注意这个和confirm不一样,不能挂起后续代码的执行,后续的代码依据点击是否来执行的话要放回调里面 解决方案二: sweetalert UI很漂亮 我自己的所有项目都用的这个