一、編譯runtime
1、Download SDK:
ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/4.0.1/source/ 下載解壓firefox-4.0.1.source.tar.bz2文件
解壓的文件夾firefox-4.0.1.source\mozilla-2.0\modules\plugin,裏面有開發NPAPI插件所需的所有資源.
2、在plugin目錄下面的sdk\samples可以找到npruntime的例子,接着根據文檔說明,一步一步建立vs2008工程
我的路徑是桌面下c:\Users\Administrator\Desktop\firefox-4.0.1.source\mozilla-2.0\modules\plugin\sdk\samples\npruntime
首先: 使用VS2008創建空的win32 dll項目,然後設置工程屬性:
步驟一:添加npruntime文件夾下面所有的.h,.cpp文件,還有.rc,.def文件
步驟二:工程設置庫路徑: 工程屬性->c/c++常規:附加包含目錄 ../../include;../../../../base/public
步驟三:工程設置預處理器:工程屬性->c/c++->預處理器:預處理器定義:WIN32;_DEBUG;_WINDOWS;_USRDLL;NPRUNTIME_EXPORTS;XP_WIN32;XP_WIN;_X86_
步驟四:工程設置預編譯頭:工程屬性->c/c++->預編譯頭:創建/使用預編譯頭:不使用預編譯頭
步驟五:建立導出文件聲明:def,指定導出接口
步驟六:將.rc文件裏面的FileDescrip,fileOpenName,internalName,OrigianalName都改成與工程統一的名稱
開始編譯:一堆錯誤,主要是一下幾個
error C3861: 'printf': identifier not found
error C2664: 'DrawTextW' : cannot convert parameter 2 from 'char [128]' to 'LPCWSTR'
error C2275: 'NPPluginFuncs' : illegal use of this type as an expression
error C2065: 'setvalue' : undeclared identifier
error C3861: 'offsetof': identifier not found
第一個錯誤:plugin.cpp 不認識printf,由於沒使用預編譯頭,因此加上#include <stdio.h>
第二個錯誤:編譯使用的是unicode字符集,因此DrawText會被按照DrawTextW來編譯,可以修改工程屬性,使用多字符集編譯或者將DrawText改爲DrawTextA
第三、四五個錯誤:setvalue offsetoff缺少,加上#include <stddef.h>
再重新編譯,ok,看到了激動的npruntime.dll
參考:https://developer.mozilla.org/en-US/docs/Compiling_The_npruntime_Sample_Plugin_in_Visual_Studio
3、檢測fireforx是否識別
將npruntime.dll拷貝到fireforx下的plugins目錄下:沒裝插件貌似沒有這個目錄,自己新建一個,放進去,打開fireforx,輸入about:plugins,看到了
npruntime scriptable example plugin
文件: npruntime.dll
版本: 1.0.0.1
npruntime
MIME 類型 描述 後綴
application/mozilla-npruntime-scriptable-plugin npruntime rts
成功。
4、用fireforx打開npruntime目錄下面的test.html目錄,奇怪,沒加載,打開test.html查看了一下,原來有個地方需要改爲:<embed type="application/mozilla-
npruntime-scriptable-plugin" style="display: block; width: 50%; height: 100px;"><br>
插件的MIME 類型是application/mozilla-npruntime-scriptable-plugin,fireforx是根據MEMETYPE來加載的。因此需要改成runtime的mime
再次打開,ok,可以點擊頁面幾個按鈕感受一下交互
例子編譯完成,有了初步的認識之後,接下來需要熟悉一下code了。
二、建立自己的插件
1、VS2008建立空的dll,工程名稱:npdemo
2、將npruntime下的相關.h,.cpp,.def拷貝過來,加入到工程中
3、加入頭文件,npruntime同級目錄下(firefox-4.0.1.sourc\mozilla-2.0\modules\plugin)include整個文件夾(包含2個.h文件)拷貝到工程目錄下面,同時將目錄
(firefox-4.0.1.source\mozilla-2.0\modules\plugin\base\public下所有文件拷入到剛纔的拷入工程的inlcude的裏面
此處就是將需要包含的目錄獨立到工程目錄下來
4、對工程進行設置並且進行編譯
參照編譯runtime。https://developer.mozilla.org/en-US/docs/Compiling_The_npruntime_Sample_Plugin_in_Visual_Studio
需要注意的是: 目錄包含先前拷過來的include就夠了,以及編譯選項和 resource的version上。當然重點還是version上面
新建resource->version文件:聲明dll版本信息。編輯version,在資源文件npdemo.rc右鍵查看代碼,進入編輯Version段
添加VALUE "MIMEType", "application/mozilla-npdemo-plugin",此id是Fireforx識別插件的標識
VALUE "FileExtents", "rts"
保存,關閉。在打開,就會看見了。
此處注意:
.rc文件必須是英文(美國)編碼040904E4,打開version
同時選中:Block Header,進行屬性編輯。(不是version node的attribute)
Language:英文(美國)
Code Page:Windows 3.1 拉丁語 1 (美國、西歐)版
VALUE "Translation", 0x409, 1252
文檔說明:https://developer.mozilla.org/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Development_Overview
5、編譯ok,Fireforx也能發現,編譯部分完工,接下來處理與js交互方面。詳見npdemo
A:js主動調用插件接口。js進行插件調用接口傳遞的時候,是通過接口id名稱來進行傳遞的,NPAPI裏面的NPIdentifier。
以test接口js調用爲例:
js 方面:
- var rtn = embed1.Test(true,456,7777,"abssssss","abcsssss\0"); //函數名稱Test 插件提供
首先:是先聲明一個全局的接口id,
- static NPIdentifier s_idTest;
然後:在plugin類初始化的時候,通過接口名稱獲取接口的id:
- s_idTest = NPN_GetStringIdentifier("Test");
其次:在ScriptablePluginObject::HasMethod(NPIdentifier name)裏面需要指明由此方法,瀏覽器調用的時候,會先進入到此查詢是否有此方法,然後再進入 ScriptablePluginObject::Invoke去調用接口方法。
- if(name == s_idTest || name == s_idTest1)
- {
- OutputDebugStringA("has method Test test1");
- return true;
- }
再次:在ScriptablePluginObject::Invoke裏面進行接口的實現,根據接口名稱判斷是否是此接口來進行對接口的實現過程
- if(name == s_idTest)
- {
- OutputDebugStringA("Test called\n");
- if(args != NULL && argCount >= 5)
- {
- //fun paramer
- NPVariant npParam1 = args[0];
- NPVariant npParam2 = args[1];
- NPVariant npParam3 = args[2];
- NPVariant npParam4 = args[3];
- NPVariant npParam5 = args[4];
- bool npbParam1 = NPVARIANT_TO_BOOLEAN(npParam1);
- //此處傳入2個int類型參數,
- //Fireforx下:npnParam2正常 npnParam3爲0
- //Chrome 下:npnParam2爲0 npnParam3正常
- //即:傳入的int參數 Fireforx下需要從int32取,chrome下需要從double下取
- int32_t npnParam2 = NPVARIANT_TO_INT32(npParam2);
- int32_t npnParam3 = NPVARIANT_TO_DOUBLE(npParam3);
- //傳入兩個string npsParam5後面增加\0,npsParam4後面不加
- //Fireforx下:都正常
- //chrome 下:加了\0的正常,不加的會有亂碼出現
- NPString npsParam4 = NPVARIANT_TO_STRING(npParam4);
- NPString npsParam5 = NPVARIANT_TO_STRING(npParam5);
- //構造string返回值 result
- char *temp = (char *)NPN_MemAlloc(1024);
- if(temp == NULL)
- return false;
- sprintf(temp,"npdemodll:bool:[%d] int[%d] int:[%d] string:[%s] string:[%s]",
- npbParam1,npnParam2,npnParam3,npsParam4.UTF8Characters,npsParam5.UTF8Characters);
- STRINGZ_TO_NPVARIANT(temp,*result);
- //return value
- //DOUBLE_TO_NPVARIANT(2437309816,*result);
- //BOOL bResult = FALSE;
- //BOOLEAN_TO_NPVARIANT(bResult,*result);
- return true;
- }
- }
B:js提供接口給插件調用,即回調。
以jsCallbackFun爲例
js方面:
- embed1.jsCallbackFun = jsfunc; //embed1爲插件對象,jsCallbackFun回調接口做爲插件對象的一個屬性,被賦值爲js的函數,有插件提供
- function jsfunc(state)
- {
- alert("jsfunc called!");
- alert(state);
- }
插件方面:
同樣聲明回調接口id:
- static NPIdentifier s_idjsCallbackFun;
同樣獲取接口名稱 :
- s_idjsCallbackFun = NPN_GetStringIdentifier("jsCallbackFun");
不同是:此接口作爲屬性提供,因此在ScriptablePluginObject::HasProperty需要聲明
- bool ScriptablePluginObject::HasProperty(NPIdentifier name)
- {
- return name == s_idjsCallbackFun;
- //return false;
- }
同時也需要在屬性設置 ScriptablePluginObject::SetProperty下設置。
需要先在cplugin類下增加一個回調接口對象的成員變量:NPObject *m_pJSCallbackFunObj;,同時提供訪問和設置屬性接口,m_pJSCallbackFunObj 需要構造爲null,不然會崩潰
- bool ScriptablePluginObject::SetProperty(NPIdentifier name,
- const NPVariant *value)
- {
- //js property
- if(name == s_idjsCallbackFun)
- {
- CPlugin *plugin = (CPlugin *)mNpp->pdata;
- if(plugin == NULL)
- return false;
- if(plugin->GetJSCallbackFunObj() == NULL)
- {
- //set js property obj
- NPObject *pObject = NPN_RetainObject(NPVARIANT_TO_OBJECT(*value));
- plugin->SetJSCallbackFunObj(pObject);
- }
- return true;
- }
- return false;
- }
然後便可以使用m_pJSCallbackFunObj作爲NPN_InvokeDefault的參數進行回調js的函數了,封裝了一下調用過程爲cplug的成員函數,可以在需要回調的時候調用
- bool CPlugin::JSCallbackFun(int32_t nState)
- {
- if (m_pJSCallbackFunObj != NULL)
- {
- NPVariant result;
- NPVariant relements[1];
- INT32_TO_NPVARIANT(nState,relements[0]);
- //invoke js fun by js obj
- NPN_InvokeDefault(m_pNPInstance,m_pJSCallbackFunObj,relements,1,&result);
- NPN_ReleaseVariantValue(&result);
- }
- return true;
- }
這段也可以直接放在test函數裏面測試回調,nodemo裏面增加了一個Test1接測試回調
關於安裝:自己提供安裝包,讓用戶一鍵安裝之後各個瀏覽器都能識別並使用,同時還要顧及到先安裝插件後安裝瀏覽器的問題,因此採用的是寫註冊表的方式,即使後安裝瀏覽器,也能發現
這裏碰到的問題記錄一下,儘管解決了,可依舊有點懵:
1:在修改runtime的時候,碰到過Fireforx下面可以正常進行調用,但是在chrome下面,about:plugins可以出現,但是調用不了的情況,後來反覆折騰,文件對比依舊沒有發現哪裏出問題,只得小心重來,後面居然好了,Fireforx羣裏面也有兄弟碰見類似問題,後來也不知道怎麼樣了,回頭再研究下
2:js調用接口傳遞整數的參數,Fireforx下會存放在NPVariant下的int中,chrome下面會存放在double中,其它的還未測試過,這個問題也是排查了很久,Fireforx下好了之後轉到在chrom下面,傳遞的int全都是0,根本就沒有值,後來逐步排查,打印出傳遞進來的數據類型,才找到數據被放到了doubled段下面,至於原因,依舊困惑,後來索性直接全部改成string
3:編碼問題,不知道是不是,剛開始在這方面找了不少原因:
js傳遞字符串參數時:Fireforx下面一切正常,到chrome下面去,會有亂碼現象,找了好久原因,各種編碼都嘗試過,自己手動強制轉也不行,google幹嘛呢這是,最後都差點要放棄了,偶然在網上看到個js字符串加上\0的,嘗試一下,去,居然好了,在回到Fireforx下面,也好了,依舊不知爲什麼,無語了。。。。。。
後話:經過一段時間的奮戰,初步完工,中間經歷了不少困難,中文資料也比較少,官方文檔英文一點點的看,遇到個一些的問題在來回折騰反覆嘗試之後,每每處在就要崩潰的邊緣的時候,迎來了柳岸花開。此時先前所有的疲憊煩惱一掃而空,一下子又滿懷鬥志,繼續前行,這種感覺相信做過coder的兄弟們你們都懂的。要不然怎會一頭扎進茫茫的code之中呢。。。。。。