簡單總結一下前幾篇文章的內容,《瀏覽器插件之ActiveX開發(一)》簡單介紹了一下如何在Vs.net 2008下用C++開發基於MFC的ActiveX插件,《瀏覽器插件之ActiveX開發(二)》介紹了開發插件時可能遇到的問題,《瀏覽器插件之ActiveX開發(三)》介紹了如何注冊插件以及如何打包成cab文件。但是,到目前為止還沒有專門提及如何在Web頁面中調用插件,本文主要針對這個問題進行展開。
一、用<Object>標簽調用ActiveX
1、Object標簽基本用法
在Html頁面中調用ActiveX插件最簡單常用的方法是:
<object id="CardAccessor" classid="clsid:03AD53E8-D7E7-485D-A39A-D07B37DEFBC9" width="0" height="0"> </object>
id屬性就不用解釋了,和html中其他元素的id一樣,是DOM樹中各元素的唯一標識。width和height表示該ActiveX在Web頁面中占位的大小,對於僅提供接口無UI界面的ActiveX來說將其設置為0即可,因為不需要在頁面上顯示任何內容(對於需要顯示界面的ActiveX,需要在項目里創建Dialog及寫相應邏輯,可以參考“A Complete ActiveX Web Control Tutorial”實例 )。
classid屬性在這里是一個非常關鍵的屬性,IE正是通過他才能正確找到要調用的ActiveX的。每個ActiveX均有一個唯一的id來表示,這就是classid,在我們創建MFC ActiveX Control項目時Vs.net 2008就幫我們生成了這個id, 可以在程序的.idl文件最下方找到這個ID值:
一般不建議手動修改程序中的這個uuid值,因為在xxxxCtrl.cpp文件中也用到了這個id值,只是表現形式不一樣罷了:
控件注冊成功后,這個classid及控件文件位置等信息均寫入注冊表了,如下所示:
當然,如果ActiveX還定義了其他屬性,也可以在<object>中以屬性的形式給他們賦值。
如果用戶的計算機已經注冊了該插件(例如通過Setup.exe方式),那么Html引用上段代碼后就可以通過js調用插件的接口和屬性了(再次提示一下,ActiveX只能在IE瀏覽器運行,也就是<object…>這段代碼在firefox等其他瀏覽器是不能正常工作的)。
<fieldset> <legend>Read Card No Testing</legend> 卡號:<input type="text" id="txtCardNo_Read" maxlength="32" class="txt disable" readonly /> <input type="button" id="btnRead" value=" Read CardNo " class="btn" onclick="javascript:readCardNo();" /> </fieldset> <fieldset> <legend>Write Card No Testing</legend> 卡號:<input type="text" id="txtCardNo_Write" maxlength="32" class="txt" /> <input type="button" id="btnWrite" value=" Write CardNo " class="btn" onclick="javascript:writeCardNo();" /> </fieldset> <script type="text/javascript"> var txtCardNo_Read = document.getElementById("txtCardNo_Read"); var txtCardNo_Write = document.getElementById("txtCardNo_Write"); var objCard = document.getElementById("CardAccessor"); function readCardNo() { txtCardNo_Read.value = ""; try{ var ret = objCard.ReadCardNo(); if (ret == 0) { txtCardNo_Read.value = objCard.CardNo; alert("讀卡成功!"); } else { alert("讀卡失敗!錯誤碼為:" + ret); } } catch (e) { alert(e.message) } } function writeCardNo() { var cardNo = txtCardNo_Write.value; try { objCard.CardNo = cardNo; var ret = objCard.WriteCardNo(); if (ret == 0) { alert("寫卡成功!"); } else { alert("寫卡失敗!錯誤碼為:" + ret); } } catch (e) { alert(e.message) } } </script>
2、使用Object標簽時如何判斷ActiveX是否已注冊
<script type="text/javascript"> var objCard = document.getElementById("CardAccessor"); if (objCard.object==null) { alert("CardAccessor插件未安裝!"); } else{ alert("已檢測到CardAccessor插件!"); } </script>
另外也可以通過訪問ActiveX中的某個已知已定義的屬性來判斷插件是否已安裝,如果返回的屬性值為undefined,則表示沒有檢測到插件。例如:
<script type="text/javascript"> var objCard = document.getElementById("CardAccessor"); if (objCard.CardNo==undefinedl) { alert("CardAccessor插件未安裝!"); } else{ alert("已檢測到CardAccessor插件!"); } </script>
3、如何讓IE自動下載安裝插件並智能升級
如果檢測到插件沒有安裝,怎樣讓IE自動從指定位置下載插件並自動安裝呢?很簡單,在object標簽中使用codebase屬性即可:
<object id="CardAccessor" classid="clsid:03AD53E8-D7E7-485D-A39A-D07B37DEFBC9" codebase="CardAccessor.cab#version=1,0,0,1" width="0" height="0"> </object>
codebase的值格式為“xxxxx.cab#version=1,0,0,1”。'#'前面部分為cab文件的位置,可以是在服務器上的絕對位置,也可以是相對位置。'#‘后面部分表示當前引用的cab包的版本號。當IE檢測到系統沒有注冊指定插件,便從codebase指定的位置下載該cab到本地,並按照其中.inf文件的描述將各文件復制到指定位置並注冊指定的控件。(注:在實際應用中涉及簽名問題,后文再述)
即使本地已經注冊了該插件,IE還將拿已注冊的控件版本號與codebase中指定的版本號相比較,如果codebase中指定的版本號大於已注冊插件的版本號,IE仍然會從codebase指定位置下載cab包並重新注冊該插件。
正如前面的文章所提,為方便管理,一般將cab包中需注冊的ocx文件的版本號視同cab版本號。
當插件或插件依賴的文件需要升級時,只需更新相應的文件,並對.inf中的相應文件版本升級(為方便管理,無論是否更新了ocx文件,該ocx文件的版本號也跟着升級,因為其版本號代表了整個cab),然后重新打包成cab發布到服務器上,並更新html中object標簽中codebase屬性值的version部分版本號。當用戶下次訪問該頁面時,IE將自動下載升級后的cab並重新注冊插件。實際上,經過我的測試,及時服務器上的cab包不做任何變化,只要增加codebase中version的值,對應插件均會重新下載和注冊。
二、通過javascript的new ActiveXObject來調用ActiveX
如果不使用object標簽,也可以直接通過js的ActiveXObject來創建指定ActiveX的實例從而達到調用插件接口的目的(IE下xmlhttpRequest的調用就是這個原理)。例如:
var objCard = new ActiveXObject("Uprain.CardAccessorCtrl.1");
如果插件已經注冊,接下來就可以通過objCard來調用插件接口和訪問屬性了。
ActiveXObject函數的參數為對應插件的ProgId而非CLASSID。在項目中xxxxCtrl.cpp文件中同樣可以找到或修改對應插件的ProgId值,如下圖:
即IMPLEMENT_OLECREATE_EX的第二參數就表示當前插件的ProgId,該值可以根據實際需要自行修改。實際上,在注冊表中通過ProgId是可以找到對應的ClassId的,兩者是有關聯的:
那么如何判斷ActiveX是否已經安裝呢?實際上如果ActiveX未安裝,通過new ActiveXObject的方式來創建插件對象是會拋出“Automation 服務器不能創建對象”異常的。所以用如下方式即可:
try { objCard = new ActiveXObject("Uprain.CardAccessorCtrl.1"); } catch (e) { alert("調用ActiveX失敗!"); }
不過,我在實際測試過程中遇到兩種情況:
1) 出現“Automation 服務器不能創建對象”異常的並不一定就表示插件沒安裝,也有可能是因插件未實現初始化或腳本安全接口,從而被IE攔截,需要調整IE“工具-選項-安全-自定義級別”中“ActiveX控件和插件”部分的設置;
2) 有時候,同樣已注冊的插件,通過object標簽引用的方式能正常調用接口,但通過new ActiveXObject的方式則調用插件接口失敗。