日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

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

發布時間:2025/3/20 HTML 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NPAPI——实现非IE浏览器的类似ActiveX的本地程序(插件)调用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文:http://blog.csdn.net/zhouyuqwert/article/details/7387516

一.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 plaincopyprint?
  • //?Microsoft?Visual?C++?generated?resource?script.??
  • //??
  • #include?"resource.h"??
  • ??
  • #define?APSTUDIO_READONLY_SYMBOLS??
  • /??
  • //??
  • //?Generated?from?the?TEXTINCLUDE?2?resource.??
  • //??
  • #include?"afxres.h"??
  • ??
  • /??
  • #undef?APSTUDIO_READONLY_SYMBOLS??
  • ??
  • /??
  • //?Chinese?(Simplified,?PRC)?resources??
  • ??
  • #if?!defined(AFX_RESOURCE_DLL)?||?defined(AFX_TARG_CHS)??
  • LANGUAGE?LANG_CHINESE,?SUBLANG_CHINESE_SIMPLIFIED??
  • ??
  • #ifdef?APSTUDIO_INVOKED??
  • /??
  • //??
  • //?TEXTINCLUDE??
  • //??
  • ??
  • 1?TEXTINCLUDE???
  • BEGIN??
  • ????"resource.h\0"??
  • END??
  • ??
  • 2?TEXTINCLUDE???
  • BEGIN??
  • ????"#include?""afxres.h""\r\n"??
  • ????"\0"??
  • END??
  • ??
  • 3?TEXTINCLUDE???
  • BEGIN??
  • ????"\r\n"??
  • ????"\0"??
  • END??
  • ??
  • #endif????//?APSTUDIO_INVOKED??
  • ??
  • ??
  • /??
  • //??
  • //?Version??
  • //??
  • ??
  • VS_VERSION_INFO?VERSIONINFO??
  • ?FILEVERSION?1,0,0,1??
  • ?PRODUCTVERSION?1,0,0,1??
  • ?FILEFLAGSMASK?0x3fL??
  • #ifdef?_DEBUG??
  • ?FILEFLAGS?0x1L??
  • #else??
  • ?FILEFLAGS?0x0L??
  • #endif??
  • ?FILEOS?0x40004L??
  • ?FILETYPE?0x2L??
  • ?FILESUBTYPE?0x0L??
  • BEGIN??
  • ????BLOCK?"StringFileInfo"??
  • ????BEGIN??
  • ????????BLOCK?"040904e4"??
  • ????????BEGIN??
  • ????????????VALUE?"CompanyName",?"WHU?ISS"??
  • ????????????VALUE?"FileDescription",?"A?new?Plugin?For?test"??
  • ????????????VALUE?"FileVersion",?"1.0.0.1"??
  • ????????????VALUE?"InternalName",?"npTest.dll"??
  • ????????????VALUE?"LegalCopyright",?"Copyright?(C)?2012"??
  • ????????VALUE?"MIMEType",?"application/x-npTest"??
  • ????????????VALUE?"OriginalFilename",?"npTest.dll"??
  • ????????????VALUE?"ProductName",?"new?Plugin?Test"??
  • ????????????VALUE?"ProductVersion",?"1.0.0.1"??
  • ????????END??
  • ????END??
  • ????BLOCK?"VarFileInfo"??
  • ????BEGIN??
  • ????????VALUE?"Translation",?0x804,?1200??
  • ????END??
  • END??
  • ??
  • #endif????//?Chinese?(Simplified,?PRC)?resources??
  • /??
  • ??
  • ??
  • ??
  • #ifndef?APSTUDIO_INVOKED??
  • /??
  • //??
  • //?Generated?from?the?TEXTINCLUDE?3?resource.??
  • //??
  • ??
  • ??
  • /??
  • #endif????//?not?APSTUDIO_INVOKED??

  • 需要注意的是Block 必須為040904e4,MIMEType為最后引用插件的標志

    4.新建一個Module-Definition File(.def),定義入口函數

    [plain] view plaincopyprint?
  • LIBRARY???npTest??
  • ??
  • EXPORTS??
  • ????NP_GetEntryPoints???@1??
  • ????NP_Initialize???????@2??
  • ????NP_Shutdown?????????@3??

  • 5.新建一個CPlugin類繼承nsPluginInstanceBase,作為插件實例類(后面再說該類的作用)

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

    類名為Cplugin,頭文件名為plugin.h,(npp_gate.cpp會使用到,不同可以修改)

    修改構造函數的實現,帶參數NPP類型并新建一個屬性保存該參數

    實現父類的三個純虛函數

    [cpp] view plaincopyprint?
  • NPBool?init(NPWindow*?aWindow);//NPWindow用于插件中繪畫部件的窗口??
  • void?shut();??
  • 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 plaincopyprint?
  • 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);??
  • ????static?bool?_removeProperty(NPObject?*npobj,?NPIdentifier?name);??
  • ????static?bool?_enumerate(NPObject?*npobj,?NPIdentifier?**identifier,uint32_t?*count);??
  • ????static?bool?_construct(NPObject?*npobj,?const?NPVariant?*args,uint32_t?argCount,?NPVariant?*result);??

  • 在PluginObject.h中聲明一個NPClass對象,使用上面的靜態方法將該NPClass對象初始化

    [cpp] view plaincopyprint?
  • #ifndef?__object_class??
  • #define?__object_class??
  • static?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方法創建該對象

    [cpp] view plaincopyprint?
  • NPObject*?CPlugin::GetScriptableObject(){??
  • ????return?NPN_CreateObject(this->instance,&objectClass);??
  • }??
  • this->instance在構造函數時獲取并保存下來的NPP對象.

    NPN_CreateObject會在瀏覽器中做一些操作然后回來調用objectClass中的_allocate方法

    需要實現該靜態方法,new 一個PluginObject

    新建一個NPP npp屬性,和一個NPP參數的構造函數

    [cpp] view plaincopyprint?
  • 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獲取

    例如:

    [cpp] view plaincopyprint?
  • PluginObject::PluginObject(NPP?npp)??
  • {??
  • ??????
  • ????this->npp?=?npp;??
  • ????id_func_add?=?NPN_GetStringIdentifier("add");??
  • ????id_property_version?=?NPN_GetStringIdentifier("version");??
  • }??
  • bool?PluginObject::hasMethod(NPObject*?obj,?NPIdentifier?methodName)??
  • {??
  • ??
  • ????if(methodName==this->id_func_add)??
  • ????????return?true;??
  • ????return?false;??
  • }??

  • 多說下enumerate方法或者說是NPN_XXX的方法,因為就這個東西折騰我完完整整的兩天時間...

    enumerate方法的參數有個指針數組,但是他的結構是

    而且初始化的時候一定要用NPN_MemAlloc來操作....API上只有關于指針數組的結構說明,而且很簡單的提了一句,折騰兩天才發現非得用NPN來分配內存- -||

    弱弱的總結下,應該是需要給Firefox用到的東西或者說從參數傳進來需要你分配內存的都得用NPN_MemAlloc分配內存

    如果出現Access Violation,首先想到什么地方應該用NPN_MemAlloc....

    [cpp] view plaincopyprint?
  • bool?PluginObject::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 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 plaincopyprint?
  • <html>??
  • ????<head>??
  • ????????<script>??
  • ????????????window.onready?=?function(){??
  • ??
  • ????????????}??
  • ????????????function?toDoSt(){??
  • ????????????????var?plugin?=?document.getElementById("plugin");??
  • ????????????????alert(plugin.version);??
  • ????????????}??
  • ????????</script>??
  • ????????<embed?id="plugin"?type="application/x-npTest"?src="file:///path?to?npTest.dll"?pluginspage="http://xxxx">??
  • ????</head>??
  • ????<body>??
  • ????????<input?type="button"?onclick="toDoSt()"?value="test">??
  • ????</body>??
  • </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

    總結

    以上是生活随笔為你收集整理的NPAPI——实现非IE浏览器的类似ActiveX的本地程序(插件)调用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。