使用NPAPI開發Fireforx/Chrome插件

一、編譯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 方面:   

[javascript] view plaincopy
  1. var rtn = embed1.Test(true,456,7777,"abssssss","abcsssss\0");   //函數名稱Test 插件提供  


      首先:是先聲明一個全局的接口id,

  1. static NPIdentifier s_idTest;  

      然後:在plugin類初始化的時候,通過接口名稱獲取接口的id:

  1. s_idTest = NPN_GetStringIdentifier("Test");  


      其次:在ScriptablePluginObject::HasMethod(NPIdentifier name)裏面需要指明由此方法,瀏覽器調用的時候,會先進入到此查詢是否有此方法,然後再進入    ScriptablePluginObject::Invoke去調用接口方法。

  1. if(name == s_idTest || name == s_idTest1)  
  2.  {  
  3.   OutputDebugStringA("has method Test test1");  
  4.   return true;  
  5.  }  

再次:在ScriptablePluginObject::Invoke裏面進行接口的實現,根據接口名稱判斷是否是此接口來進行對接口的實現過程
 

  1. if(name == s_idTest)  
  2.   {  
  3.    OutputDebugStringA("Test called\n");  
  4.    if(args != NULL && argCount >= 5)  
  5.    {  
  6.     //fun paramer  
  7.     NPVariant npParam1   = args[0];  
  8.     NPVariant npParam2   = args[1];  
  9.     NPVariant npParam3   = args[2];  
  10.     NPVariant npParam4   = args[3];  
  11.     NPVariant npParam5   = args[4];  
  12.     bool npbParam1 = NPVARIANT_TO_BOOLEAN(npParam1);  
  13.     //此處傳入2個int類型參數,  
  14.     //Fireforx下:npnParam2正常 npnParam3爲0  
  15.     //Chrome  下:npnParam2爲0  npnParam3正常  
  16.     //即:傳入的int參數 Fireforx下需要從int32取,chrome下需要從double下取  
  17.     int32_t  npnParam2 = NPVARIANT_TO_INT32(npParam2);  
  18.     int32_t  npnParam3 = NPVARIANT_TO_DOUBLE(npParam3);  
  19.     //傳入兩個string  npsParam5後面增加\0,npsParam4後面不加  
  20.     //Fireforx下:都正常  
  21.     //chrome  下:加了\0的正常,不加的會有亂碼出現  
  22.     NPString npsParam4 = NPVARIANT_TO_STRING(npParam4);  
  23.     NPString npsParam5 = NPVARIANT_TO_STRING(npParam5);  
  24.     //構造string返回值 result  
  25.     char *temp = (char *)NPN_MemAlloc(1024);  
  26.     if(temp == NULL)  
  27.      return false;  
  28.     sprintf(temp,"npdemodll:bool:[%d] int[%d] int:[%d] string:[%s] string:[%s]",  
  29.      npbParam1,npnParam2,npnParam3,npsParam4.UTF8Characters,npsParam5.UTF8Characters);  
  30.     STRINGZ_TO_NPVARIANT(temp,*result);  
  31.     //return value  
  32.     //DOUBLE_TO_NPVARIANT(2437309816,*result);  
  33.     //BOOL bResult  = FALSE;  
  34.     //BOOLEAN_TO_NPVARIANT(bResult,*result);  
  35.   
  36.     return true;  
  37.    }  
  38.   }  

 

B:js提供接口給插件調用,即回調。

      以jsCallbackFun爲例

     js方面:

[javascript] view plaincopy
  1. embed1.jsCallbackFun = jsfunc;    //embed1爲插件對象,jsCallbackFun回調接口做爲插件對象的一個屬性,被賦值爲js的函數,有插件提供  
  2.   
  3.   
  4.      function jsfunc(state)  
  5.     {  
  6.          alert("jsfunc called!");  
  7.          alert(state);  
  8.     }  


 

     插件方面:

     同樣聲明回調接口id:         

  1. static NPIdentifier s_idjsCallbackFun;  

     同樣獲取接口名稱 :         

  1. s_idjsCallbackFun = NPN_GetStringIdentifier("jsCallbackFun");  


     不同是:此接口作爲屬性提供,因此在ScriptablePluginObject::HasProperty需要聲明

  1. bool  ScriptablePluginObject::HasProperty(NPIdentifier name)  
  2.   {  
  3.        return name == s_idjsCallbackFun;  
  4.        //return false;  
  5.   }  

 

 同時也需要在屬性設置 ScriptablePluginObject::SetProperty下設置。

 需要先在cplugin類下增加一個回調接口對象的成員變量:NPObject *m_pJSCallbackFunObj;,同時提供訪問和設置屬性接口,m_pJSCallbackFunObj 需要構造爲null,不然會崩潰

  1. bool   ScriptablePluginObject::SetProperty(NPIdentifier name,  
  2.          const NPVariant *value)  
  3. {  
  4.  //js property  
  5.  if(name == s_idjsCallbackFun)  
  6.  {  
  7.   CPlugin *plugin = (CPlugin *)mNpp->pdata;  
  8.   if(plugin == NULL)  
  9.    return false;  
  10.   if(plugin->GetJSCallbackFunObj() == NULL)  
  11.   {  
  12.    //set js property obj  
  13.    NPObject *pObject = NPN_RetainObject(NPVARIANT_TO_OBJECT(*value));  
  14.    plugin->SetJSCallbackFunObj(pObject);  
  15.   }  
  16.   return true;  
  17.  }  
  18.  return false;  
  19. }  

然後便可以使用m_pJSCallbackFunObj作爲NPN_InvokeDefault的參數進行回調js的函數了,封裝了一下調用過程爲cplug的成員函數,可以在需要回調的時候調用

  1. bool CPlugin::JSCallbackFun(int32_t nState)  
  2. {  
  3.  if (m_pJSCallbackFunObj != NULL)  
  4.  {  
  5.   NPVariant result;   
  6.   NPVariant relements[1];  
  7.   INT32_TO_NPVARIANT(nState,relements[0]);  
  8.   //invoke js fun by js obj  
  9.   NPN_InvokeDefault(m_pNPInstance,m_pJSCallbackFunObj,relements,1,&result);  
  10.   NPN_ReleaseVariantValue(&result);  
  11.  }  
  12.  return true;  
  13. }  

這段也可以直接放在test函數裏面測試回調,nodemo裏面增加了一個Test1接測試回調 

 下載源碼

關於安裝:自己提供安裝包,讓用戶一鍵安裝之後各個瀏覽器都能識別並使用,同時還要顧及到先安裝插件後安裝瀏覽器的問題,因此採用的是寫註冊表的方式,即使後安裝瀏覽器,也能發現

詳細參照:https://developer.mozilla.org/en-US/docs/Plugins/The_First_Install_Problem?redirectlocale=en-US&redirectslug=Plugins%3A_The_First_Install_Problem

這裏碰到的問題記錄一下,儘管解決了,可依舊有點懵:

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之中呢。。。。。。

發佈了24 篇原創文章 · 獲贊 51 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章