瀏覽器外掛程式之ActiveX開發(一)

來源:互聯網
上載者:User

     一般的Web應用對於瀏覽器外掛程式能不使用的建議盡量不使用,因為其涉及到安全問題以及影響使用者安裝(或自動下載註冊安裝)體驗問題。在有特殊需求(如涉及資料安全的金融業務資料互動、需外掛程式才能實現的與本地裝置的互動等)的情況下可以酌情慎用。

     瀏覽器外掛程式總體可以劃分為兩大陣營,即IE支援的外掛程式以及非IE支援的外掛程式。本來在Netscape時代,對於瀏覽器外掛程式是有公用的規範的(NPAPI),一開始所有瀏覽器都支援該規範,包括IE。後來出於商業原因,微軟的IE不再支援NPAPI,改而自己開發了一套基於COM的ActiveX體系,但這個體系對於非IE瀏覽器是拒絕支援的。所以目前的狀況基本是,IE瀏覽器僅支援ActiveX控制項,而Firefox、Chrome等瀏覽器只支援另一類介面(XPCOM或NPAPI)。要想實現一個Web外掛程式,至少需要同時考慮IE支援的AceiveX版以及非IE支援的Plugin版(Flash等外掛程式對於IE與非IE瀏覽器都是不同的)。

     ActiveX的開發可以用C#、VB及C++等語言。用C++開發ActiveX既可以使用ATL,也可以使用MFC。ATL ActiveX輸出檔案較小,適合網路傳輸,但開發複雜度稍大;而MFC ActiveX輸出檔案稍大(附帶必要的MFC dll),但易於上手。本文主要介紹基於MFC的ActiveX開發。

一、建立項目及添加介面

     在Vs.net 2008中,建立一個MFC ActiveX Control項目:

                          

    點擊“OK”後將彈出如下對話方塊:

                          

    依次點擊“Next”按鈕直到“Control Settings”標籤頁:

                          

     由於本例子只示範僅提供函數介面不基於介面的ActiveX,故“Create control based on”選擇“(none)”即可。點擊"Finish”按鈕,即完成了項目的建立,檔案結構如下:

                           

    右擊項目名稱,選擇“Properties”,在項目屬性對話方塊中對“All Configurations”進行配置。在“Configurations Properties->General”標籤頁中,“Use of MFC”選擇“Use MFC in a static Library”,以便編譯時間將MFC相關庫自動和控制項一起打包。對於“Character Set”的選擇根據具體情況而定,須注意“Unicode Character Set”和“Mulity-Byte Character SEt”對字元處理是完全不一樣的(字元編碼不一樣,需要進行MultiByteToWideChar或WideCharToMultiByte轉換)。

注意:建立MFC ActiveX Control時已經自動給項目添加了.def檔案並做好了相應關聯。若對配置資訊更改後導致編譯的ocx註冊不成功或提示找不到EntryPoint,可以檢查一下Linker->Input的Module Definition File是否配置正確,正常情況下已經自動設定好了,如:

                  

 

   接下來就可以在ActiveX中添加我們需要與外部互動的介面方法和屬性了。選擇“Class View”,右擊“MyTestActiveXLib->_DMyTestActiveX”,在彈出的菜單中可以選擇Add Function或Add Property來添加介面方法或介面屬性:

                  

   這裡以定義一個LONG AddFun(LONG num1,LONG num2) 的介面函數為例,添加Menthod如所示:

                  

    點擊Finish後,即可在“MyTestActiveXCtrl.cpp”檔案找到剛添加的介面函數代碼:

                

     在函數體中完成自訂的商務邏輯即可。

 

二、實現安全介面

      上述項目編譯後即可產生ocx檔案,該ocx即可嵌入html在IE中運行。但如果該ocx對應頁面是放在真實的web伺服器上,訪問該頁面執行ActiveX裡對應介面時IE將會提示“無相關屬性,需要設定其初始化和指令碼啟動並執行安全性”等資訊。這是因為ActiveX要在遠程IE上執行,需要實現安全介面。有關控制項的初始化和指令碼安全問題,《再談IObjectSafety》一文及其引用的Microsoft文章做了較詳致描述。

      對於ATL寫的ActiveX,實現IObjectSafety即可,這裡有ATL實現安全介面的詳細的描述。

      對於MFC寫的ActiveX,可以通過修改註冊表的方式來實現控制項的安全性,微軟也提供的詳細的文檔描述。具體實現步驟如下:

      1、首先在項目中添加Cathelp.h和Cathelp.cpp兩個檔案,其內容如下所示。

      Cathelp.h

#include "comcat.h"// Helper function to create a component category and associated// descriptionHRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);// Helper function to register a CLSID as belonging to a component// categoryHRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);// HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid);

      Cathelp.cpp

#include "stdafx.h"#include "comcat.h"#include "strsafe.h"#include "objsafe.h"// HRESULT CreateComponentCategory - Used to register ActiveX control as safe HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription){    ICatRegister *pcr = NULL ;    HRESULT hr = S_OK ;     hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,             NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);    if (FAILED(hr))        return hr;     // Make sure the HKCR\Component Categories\{..catid...}    // key is registered.    CATEGORYINFO catinfo;    catinfo.catid = catid;    catinfo.lcid = 0x0409 ; // english    size_t len;    // Make sure the provided description is not too long.    // Only copy the first 127 characters if it is.    // The second parameter of StringCchLength is the maximum    // number of characters that may be read into catDescription.    // There must be room for a NULL-terminator. The third parameter    // contains the number of characters excluding the NULL-terminator.    hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, &len);    if (SUCCEEDED(hr))        {        if (len>127)          {            len = 127;          }        }       else        {          // TODO: Write an error handler;        }    // The second parameter of StringCchCopy is 128 because you need     // room for a NULL-terminator.    hr = StringCchCopy(catinfo.szDescription, len + 1, catDescription);    // Make sure the description is null terminated.    catinfo.szDescription[len + 1] = '\0';     hr = pcr->RegisterCategories(1, &catinfo);    pcr->Release();     return hr;} // HRESULT RegisterCLSIDInCategory -//      Register your component categories information HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid){// Register your component categories information.    ICatRegister *pcr = NULL ;    HRESULT hr = S_OK ;    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,                 NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);    if (SUCCEEDED(hr))    {       // Register this category as being "implemented" by the class.       CATID rgcatid[1] ;       rgcatid[0] = catid;       hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);    }     if (pcr != NULL)        pcr->Release();                return hr;} // HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid){    ICatRegister *pcr = NULL ;    HRESULT hr = S_OK ;     hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,             NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);    if (SUCCEEDED(hr))    {       // Unregister this category as being "implemented" by the class.       CATID rgcatid[1] ;       rgcatid[0] = catid;       hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);    }     if (pcr != NULL)        pcr->Release();     return hr;}

    :Cathelp.cpp中的代碼是基於Unicode Character Set的。故項目配置時若改成Multi-Byte Character Set,需對Cathelp.cpp中代碼做相應修改,否則編譯不過;

 

     2、在MyTestActiveX.cpp檔案中,添加CLSID_SafeItem的定義:

        

     CLSID_SafeItem的值是根據xxxCtrl.cpp(本例中是MyTestActiveXCtrl.cpp)檔案中IMPLEMENT_OLECREATE_EX的定義而來的(實際上就是ActiveX的CLASSID)。本例中MyTestActiveXCtrl.cpp檔案中IMPLEMENT_OLECREATE_EX的的值如下:

        

     將“0x1345c26b, 0xe979, 0x45a5, 0x99, 0x7d, 0x94, 0x27, 0xfb, 0x81, 0xe7, 0x7”簡單的在適當位置添加“{”和“}”括弧即變成了CLSID_SafeItem的值“0x1345c26b, 0xe979, 0x45a5, {0x99, 0x7d, 0x94, 0x27, 0xfb, 0x81, 0xe7, 0x7}”。

      另外,MyTestActiveX.cpp檔案起始處還需要引入如下兩個檔案方能正常編譯:

            

   

    3、修改MyTestActiveX.cpp中DllRegisterServerDllUnregisterServer函數,代碼如下(照抄即可):

// DllRegisterServer - Adds entries to the system registrySTDAPI DllRegisterServer(void){    HRESULT hr;    // HResult used by Safety Functions     AFX_MANAGE_STATE(_afxModuleAddrThis);     if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))      return ResultFromScode(SELFREG_E_TYPELIB);     if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))      return ResultFromScode(SELFREG_E_CLASS);     // Mark the control as safe for initializing.                                                 hr = CreateComponentCategory(CATID_SafeForInitializing,          L"Controls safely initializable from persistent data!");    if (FAILED(hr))      return hr;     hr = RegisterCLSIDInCategory(CLSID_SafeItem,          CATID_SafeForInitializing);    if (FAILED(hr))        return hr;     // Mark the control as safe for scripting.     hr = CreateComponentCategory(CATID_SafeForScripting,                                  L"Controls safely  scriptable!");    if (FAILED(hr))        return hr;     hr = RegisterCLSIDInCategory(CLSID_SafeItem,                         CATID_SafeForScripting);    if (FAILED(hr))        return hr;     return NOERROR;}// DllUnregisterServer - Removes entries from the system registrySTDAPI DllUnregisterServer(void){    AFX_MANAGE_STATE(_afxModuleAddrThis);      // 刪除控制項初始化安全入口.       HRESULT hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);      if (FAILED(hr))          return hr;      // 刪除控制項指令碼安全入口       hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);      if (FAILED(hr))          return hr;      if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))          return ResultFromScode(SELFREG_E_TYPELIB);      if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))          return ResultFromScode(SELFREG_E_CLASS);      return NOERROR;}

  : 很多例子裡DllUnregisterServer的寫法與本文的寫法不一致,結果導致卸載控制項時(regsvr32 /u xxxx.ocx)出現“調用某某ocx檔案的DllUnregisterServer函數出錯,錯誤碼:0x80070002”錯誤。究其根源,是DllUnregisterServer中刪除註冊表的順序出了問題,“waxgourd0的專欄”中有篇文章對此做了詳盡描述。

 

    4、在解決方案下點擊資源檔(Resources->MyTestActiveX.rc),點擊右鍵在彈出的菜單中選擇“View Code”, 編輯資源檔資訊並確保以下幾個項目的正確性:

         

        a) BLOCK的值為“040904e4

        b) OLESelfRegister的值為“\0

        c) VarFileInfo中的Translation後對應為“0x0409, 1252

 

      到目前為止,可以編譯項目,輸出的ocx控制項是可以正常啟動並執行了。~~~

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MFC ActiveX開發參考資料:

  1. A Complete ActiveX Web Control Tutorial 
  2. Enable an MFC ActiveX Control to Self-Register

ATL ActiveX開發參考資料:

  1. 洞庭散人: COM組件開發實踐(二)

綜合參考資料:

  1. 楊峰- COM組件與設計應用
  2. COM開發推薦書籍(洞庭散人推薦)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.