瀏覽器插件之ActiveX開發(二)

原文鏈接:http://www.cnblogs.com/qguohog/archive/2013/01/23/2873655.html


    按照上文《瀏覽器插件之ActiveX開發(一)》的步驟,能開發一個基於MFC的簡單的ActiveX控件。不過在實際操作中還是會遇到一些問題。由於對COM編程瞭解得很少很少,有些問題我也沒有找到很好的解決方法。

 

     一、ActiveX需要引用其他dll的問題

      我們的ActiveX需要對IC卡設備進行讀寫,所以需要調用設備自帶的接口。設備廠商提供了“mwhrf_bj.lib”、“mwhrf_bj.dll”和“mwrf32.h”等接口文件。將“mwhrf_bj.lib”和“mwrf32.h”添加到項目中,ActiveX的接口方法中就可以調用接口文件中的方法了。但是在編譯時會出現“Project:error PRJ0050:未能註冊輸出。請嘗試啓用“每個用戶的重定向”,或用提升權限從命令提示窗口中註冊該組件”或“Project : error PRJ0050: Failed to register output. Please ensure you have the appropriate permissions to modify the registry”的錯誤。

     實際上該錯誤不是出現在編輯階段,而是出現在註冊編譯後的ocx文件時。Vs.net 2008默認在編譯成功後會自動註冊編譯後的ocx文件。右擊項目名稱,選擇“Properties”,在彈出對話框的“Configurations Properties->Linker->General”中的Register Output就可以配置編譯後是否自動註冊ocx,如下圖所示:

       image

      之所以註冊ocx時出錯,是因爲註冊時找不到被調用的“mwhrf_bj.dll”文件。將被調用的“mwhrf_bj.dll”文件放在ocx文件相同目錄下或者其他%PATH%路徑下(如Windows文件夾或System32文件夾等),則註冊ocx時不會報錯。在vs.net開發環境中可以直接將要被調用的外部dll文件copy到Debug或Release目錄下即可,也可以在PreBuild腳本里將外部dll文件COPY到編譯目標文件夾,如:

       image

 

       注:可參考“http://www.cnblogs.com/lidabo/archive/2012/07/16/2593604.html”文章。

 

     二、ActiveX的調試方法

        在Vs.net 2008下可以對ActiveX按如下方式進行調試:

        1、準備好Demo.html文件並寫好測試程序,該頁面中需通過<object />來引用需測試的ocx控件(關於如何在html頁面中調用控件在後續文章將專門提及)。

        2、在vs.net 2008中右擊項目名稱,選擇“Properties”,在彈出框中的Debugging配置頁裏配置好CommandCommandArgs參數:

            Command:        本地IE瀏覽器的路徑,如“C:\Program Files\Internet Explorer\IEXPLORE.EXE

            Command Args: 已經創建好的用於測試ocx的html文件路徑(如上面提及的Demo.html文件路徑)

            image

        3、在程序中需調試的地方設置斷點。按F5運行後vs.net將自動啓動IE並打開對應的html測試文件,在斷點處會中斷運行進入調試狀態。

 

      三、ActiveX的接口實現out/ref參數及返回自定義結構體數據的問題

     有時候ActiveX的接口方法只返回一個數據並不能滿足我們的實際要求。例如通過ActiveX的getPersons()方法返回一堆的人員信息,那必定是一個列表或數組,而且每個Person還包含姓名、性別等各種信息,這個時候返回值就相當複雜了。

     爲了簡單起見,還是已通過ActiveX進行讀卡號來舉例。一般情況下,只要該插件提供以下接口即可滿足需求:

BSTR ReadCardNo();

     這樣在javascript中調用該ActiveX的ReadCardNo()方法即可返回一個包含卡號的字符串。

     但是,僅僅提供這個接口如何來識別讀卡過程中出現的異常呢?如果讀卡操作一切正常,返回一個卡號字符串當然沒有問題。但如果讀卡過程中出現諸如讀卡設備未正確連接、卡無法識別等情況,如果將這些異常信息反饋給調用者呢?

     1、首先我想到的是使用ref或out參數來解決,對應C++裏是OUT/RETVAL之類的參數修飾符。

     在.idl中定義接口爲:

      [id(1), helpstring("方法ReadCardNo")] LONG GetSheetName([out]BSTR* cardNo);
     對應接口原型爲:

      LONG ReadCardNo( BSTR* cardNo );

     這樣的話通過LONG類型的返回值來識別返回狀態,例如可以約定:

           0-讀卡成功

           1-讀卡設備未連接

           2-未找到可識別的卡

           ……

      如果返回值爲0,表示讀卡成功,讀出的卡號已通過out類型的參數cardNo傳遞給調用者。

      但是,javascript等腳本語言並不支持out/ref等類型的參數,函數參數也無法傳址,所以這個方案無法解決我的實際問題。

 

      2、如果ActiveX的接口能返回一個自定義的結構體類型數據就能滿足我們的需求了。例如我們定義一個結構體:

          typedef struct

           {

               LONG ResultStatus,              // 返回狀態  0-讀卡成功  1-讀卡設備未連接 3-未找到可識別的卡

               BSTR CardNo                       // 讀卡成功時,保存讀取的卡號

           } AOPResult;

          對應接口如果可以按如下樣子來實現就可以解決我們的問題了:

          AOPResult ReadCardNo();

         但是,在MFC ACtiveX的接口定義中中不能直接使用自定義的數據類型的,需要用VARIANT類型來進行轉換。下面幾篇參考文章均對此有所描述:

          a) http://bbs.csdn.net/topics/320146859

          b) http://bbs.csdn.net/topics/20064135

          c) http://www.codeproject.com/Articles/916/Using-User-Defined-Types-in-COM-ATL

          d) 標準MFC WinSock ActiveX控件開發實例(II)高級篇

          但實現起來也不是那麼容易,鑑於時間問題及我們實際需求的不迫切性,我對此沒有做過多嘗試。如果有成型實例,望請賜教。

       

      3、既然在Web應用場景下ActiveX的接口一般都是供js調用,那麼我們可以返回一個json類型的數據即可,如“{ status:0, cardNo:234234344634 }”。這樣ActiveX接口仍然只需返回一個BSTR的參數,只是返回值的意義變了,不是簡單的卡號,而需要ActiveX的ReadCardNo接口在內部處理時需要將返回值封裝成一個json格式的字符串返回並交由調用方解析。不過,在封裝json字符串時需要對{、}、:等特殊字符進行相應處理。

 

      4、對於簡單的應用場景,我們也完全可以利用ActiveX的屬性來化解此類問題。例如我們在ActiveX中定義一個屬性CardNo,這樣的話提供的接口只用簡單的返回一個狀態即可:

         LONG ReadCardNo()

       接口返回值仍然表示狀態,如0表示讀卡成功,1表示未找到讀卡設備等等。當返回0時,讀卡成功,對應的卡號從屬性CardNo中獲取即可。

 

 

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

參考資料:

ActiveX Controls( MFC )


=======================================================================
野文(Jasson Qian)
------------------------------------------------------
博客園:http://qguohog.cnblogs.com
CSDN:http://blog.csdn.net/sallay

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章