用ATL開發和部署ActiveX網頁控件
摘 要 ActiveX插件技術廣泛的運用于B/S系統中,本文通過一個項目執行個體,詳細介紹用ATL開發和部署ActiveX網頁控件的過程。學習使用ActiveX讓浏覽器通路用戶端的硬體資源。
關鍵字 ATL,ActiveX控件,COM元件
一、前言
在B/S結構的系統中,出于安全性考慮一般不準許浏覽器通路用戶端的硬體資源,如控制列印機,照相機等。對于一個完善系統來說,往往很多時候又需要控制這些資源。通過在浏覽器中插入ActiveX插件是一種很好的解決方式。
在實際的項目開發中,遇到系統登入需要增加實體身份識别。即在系統登入的時候,除了要驗證使用者名和密碼外,還需要驗證硬體USB KEY上的資訊。具體業務流程為:用戶端程式讀取使用者硬體USB KEY裡的個人資訊(即加密認證資訊),送出給認證伺服器進行認證,認證伺服器通過身份識别後,業務系統通過解析傳回的XML資訊判斷使用者是否合法有效,建立起使用者和業務系統的信任通道。讀取硬體USB KEY的資訊我們通過本例的ActiveX控件來完成。硬體USB KEY選用飛天誠信的ePass1000ND産品。
二、概念
1、ActiveX控件
ActiveX是Microsoft提出的一組使用COM(Component Object Model,元件對象模型)使得軟體元件在網絡環境中進行互動的技術集。它與具體的程式設計語言無關。作為針對Internet應用開發的技術,ActiveX被廣泛應用于WEB伺服器以及用戶端的各個方面。
ActiveX是從Microsoft的複合文檔技術—OLE成長起來的。其基本的出發點是想讓某個軟體通過一個通用的機構為另一個軟體提供服務,可以将其插入到WEB網頁或其它應用程式中。在Internet上的使用,ActiveX特點是:一般軟體需要使用者單獨下載下傳然後執行安裝,而ActiveX插件是當使用者浏覽到特定的網頁時,IE浏覽器即可自動下載下傳并提示使用者安裝。 但安裝的一個前提是必須經過使用者的同意及确認。
2、COM技術
COM是Microsoft元件對象模型的簡稱。是一個說明如何建立可動态交替更新元件的規範。它提供了客戶群組件為保證能夠互操作應該遵循的标準。該标準對于元件架構的重要性同其他任何一個具有可交替更新部分的系統是一樣的。
COM标準包括規範和實作兩大部分,規範部分定義了元件群組件之間通信的機制,這些規範不依賴于任何特定的語言和作業系統,隻要按照該規範,任何語言都可以使用;COM标準的實作部分是COM庫,COM庫為COM規範的具體實作提供了一些核心服務。
在COM模型中,對象本身對于客戶來說是不可見的,客戶請求服務時,隻能通過接口進行。一般接口是不會改變的。
3、ATL技術
ATL(Active Template Library)是微軟的活動模闆庫,是一個産生C++/COM代碼的架構,專門用于開發COM元件。ATL提供了小巧、高效、靈活的類,這些類為建立可互操作的COM元件提供了基本的設施。ATL完全面向COM元件,其結構完全針對COM中的諸多規範。是編寫COM元件的快捷工具。
三、實作
1、項目
打開Visual Studio.Net 2005,建立一個解決方案或項目DeanUSBKey。在項目類型中選擇Visual C++下的ATL選項,在模闆中選擇”ATL Project”,項目名為DeanUSBKey。點選确定,系統就在指定的目錄下建立了DeanUSBKey項目和解決方案。
點選确定後,會出現建立ATL項目向導對話框,引導使用者快捷友善的建立ATL項目。點選下一步,進入項目屬性設定對話框,如圖1所示。可以通過該對話框選擇是否屬性化和釋出方式等。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLi0zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwkzX39GZhh2csATMflHLwEzX4xSZz91ZsADMx8FdsYkRGZkRG9lcvx2bjxSa2EWNhJTW1AlUxEFeVRUUfRHelRHL2EzXlpXazxyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3PnVGcq5iN5QTM0UGO5czY5QTNzAzNjNTYlFDOwYTY4YjYjFzYh9CX0AzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL0M3Lc9CX6MHc0RHaiojIsJye.jpeg)
圖1 項目屬性設定對話框
具體選項說明如下:
Attributed 即屬性化,支援屬性化程式設計,是未來的發展方向,是IDL方案的一種替代方案。
Dynamic-link library(DLL) 即動态連結庫,表示建立一個 DLL 的元件程式。
Executable(EXE) 即可執行檔案,表示建立一個 EXE 的元件程式。
Service(EXE) 即服務,表示建立一個系統服務元件程式,系統啟動後就會加載并執行的程式。
Allow merging of proxy/stub code 即允許合并代理/存根代碼,選擇該項表示把“代理/存根”代碼合并到元件程式中,否則需要單獨編譯,單獨注冊代理存根程式。
Support MFC 即支援 MFC,建議不要選擇,除非有特殊的原因,比如我們原來的程式是基于MFC的,我們的元件必須要MFC的支援。一般在寫 ATL 程式,不選擇該項。但是很多VC程式員對于MFC的資料集合類和字元串類依賴很大,建議采用STL中的相關類進行替代。具體替換方案:
1、std::string代替MFC中的CString;
2、std::vector代替MFC的數組類如CArray,CPtrArray等;
3、std::list替換MFC中的CList等清單類;
4、對于BSTR建議采用CComBSTR 類,或_bstr_t類,本例子中就會用到該類;
Support COM+1.0 支援事務處理的 COM+ 功能。
我們選擇如圖1所示的選項,點選完成。ATL Project項目就生成好了,系統會在指定目錄下生成一系列檔案,ReadMe.txt裡有各檔案的檔案說明。尤其要注意接口定義語言檔案(DeanUSBKey.idl),它描述了對象的接口細節。
2、元件
在COM模型中,客戶請求服務時,是通過接口群組件進行互動的。現在還是一個空的ATL項目,還沒有任何元件。
添加元件,也就是添加ATL對象類。在DeanUSBKey項目上點選右鍵,添加類,彈出對話框。在類别中選擇ATL。在模闆中選擇“ATL Simple Object”即ATL簡單對象。點選确定,出現建立元件向導。如圖2所示,在Short Name輸入元件名稱USBKey,其它内容系統會自動填寫。注意元件名稱不能和項目名稱重名。點選下一步進入元件選項設定界面。如圖3所示
圖3 ATL簡單對象元件選項對話框
Threading model 即線程模型,COM中的線程,這是一個複雜的部分。我們選"單元"(Apartment),它代表當線上程中調用元件函數的時候,這些調用會排隊進行。如果想了解詳細細節可以參看《COM技術内幕》一書。
Interface 即接口,雙重(Dual),雙重接口表示在一個接口中,同時支援自定義接口和 IDispatch 接口。這個非常重要,為了能夠使元件能夠在腳本中使用,必須選擇雙重接口選項。因為腳本語言的解釋器隻認識IDispatch 接口。自定義接口(Custom),直接實作的是IUnknown接口。
Aggregation 即聚合,寫的元件,将來是否允許被其他人以聚合方式(有聚合和包容兩種方式)使用。Only(隻能建立為聚合),有點類似 C++或Java 中的不能直接建立執行個體的虛類,如果不是處于設計目的,一般這個選項不用。大多數情況下支援“聚合”,是以我們選擇“Yes”。
ISupportErrorInfo 是否支援豐富資訊的錯誤處理接口。
Connection points 即連接配接點,是否支援連接配接點接口(事件、回調)。
IObjectWithSite 是否支援IE的調用。
我們選擇如圖3所示的選項,點選完成。USBKey的元件建立完成。在生成的USBKey.cpp裡将是接口IUSBKey的實作。
3、接口方法
在類視圖中,IUSBKey接口上點選滑鼠右鍵。在添加項裡有添加方法和屬性,選擇添加方法。打開圖4所示的添加接口方法對話框。添加接口方法GetContent,并添加接口方法的參數。[in]表示參數方向是輸入;[out]表示參數方向是輸出;[out,retval]表示參數方向是輸出,同時可以作為函數運算結果的傳回值。一個函數中,可以有多個[in]、[out],但[retval]隻能有一個,并且要和[out]組合後在最後一個位置。詳細的定義說明可以參考IDL的文法說明。
圖4 添加接口方法對話框
在USBKey.cpp檔案裡添加函數GetContent的具體實作過程。核心代碼如下:
STDMETHODIMP CUSBKey::GetContent(LONG lFlags, BSTR* pUSBContent)
{
EPAS_STATUS retval;//狀态
EPAS_HANDLE epsHandle ; //EPAS句柄
// 建立裝置句柄
retval = epas_CreateContext(&epsHandle,0,EPAS_API_VERSION);//調用EPAS的API函數通路USB Key硬體
if (FT_SUCCESS != retval)
{return ReturnError(retval);}//傳回相應的錯誤
// 打開裝置
retval = epas_OpenDevice(epsHandle,lFlags,(void *)szAppID);
if (FT_SUCCESS != retval)
{return ReturnError(retval);}
//得到序列号
unsigned long sn[2] = {0};
retval = epas_GetProperty(epsHandle,EPAS_PROP_SERNUM,NULL,sn,sizeof(sn));
{return ReturnError(retval);}
char m_sn [8*1024+17]={0};
sprintf_s(m_sn, "%08X%08X", sn[1], sn[0]);//以16進制列印到字元串m_sn中
//得到加密字元串
//1、登入
char s[80] = "1234";//登入密碼
retval = epas_Verify(epsHandle,EPAS_VERIFY_USER_PIN,(unsigned char*)s,4);
if (FT_SUCCESS != retval){return ReturnError(retval);}
//2、打開檔案
EPAS_FILEINFO epsFileInfo = {0};
unsigned long epsFileID = 0x1234;//檔案編号
retval = epas_OpenFile(epsHandle,0,epsFileID,&epsFileInfo,sizeof(epsFileInfo));
if (FT_SUCCESS != retval){return ReturnError(retval); }
//3、讀取檔案内容
unsigned long rLen = 0;
unsigned char rBuff[8*1024] = {0};
ZeroMemory(rBuff,8*1024);
retval = epas_Read(epsHandle,0,0,rBuff,epsFileInfo.ulFileSize,&rLen);
//4、關閉檔案
retval = epas_CloseFile(epsHandle);
//關閉裝置,删除Context
retval = epas_CloseDevice(epsHandle);
retval = epas_DeleteContext(epsHandle);
strcat_s(m_sn,(char*)rBuff);
*pUSBContent=_com_util::ConvertStringToBSTR((char *)r_sn);
return S_OK;
}
為了能在函數中使用USBKey廠家提供的通路函數和使用BSTR類,需在stdafx.h頭檔案裡面引入相應的.h和.lib檔案。如下:
#include "FT_ND_API.h"// ePass1000ND的接口頭檔案
#include "comutil.h"
#pragma comment(lib, " FT_ND_API.lib")
#pragma comment(lib, "comsuppw.lib")
如果編譯通過,VS.Net IDE會打開視窗選擇執行控件的外部檔案。選擇regsvr32。編譯成功後,元件會自動注冊。可以在系統元件服務裡面檢視剛注冊的元件DeanUSBKey。
4、錯誤處理
COM方法通過傳回HRESULT來報告錯誤,其他資訊異常可以通過 IErrorInfo 接口提供給用戶端,這裡主要講述HRESULT傳回COM方法錯誤。
HRESULT由一個 32 位代碼組成。分為四部分,如下:
Field | Severity | Reserved | Facility | Code |
Bit(s) | 31 | 29-30 | 16-28 | 0-15 |
各字段說明:
Severity 字段是其中最重要的一個。當一個方法傳回時若該字段被設定了值,就說明發生了一個錯誤。該字段使所有的 COM 錯誤代碼顯示為負的十進制整數。
Reserved 字段目前是預留字段。
Facility 字段為錯誤類别代碼,總共表示 8192 種錯誤,由一個集中的機構負責配置設定這些種類。
Code 字段提供了一個可容納 65536 個代碼的空間。具體的錯誤代碼就在該字段裡面展現。
在讀取USBKey資訊時,捕獲的錯誤做處理,以COM錯誤的形式抛出。即把前16位改為0x80FF,代碼如下:
LONG CUSBKey::ReturnError(LONG retval)
{ return 0x80FF0000+retval;}
5、實作IObjectSafety接口
ActiveX控件的編寫到此就可以結束了,但我們在浏覽器使用改控件的過程中,經常都會彈出現在運作的腳本不安全的提示。如果給客戶使用,将會帶來極大不便。怎麼解決呢,可以通過實作IObjectSafety接口來解決。ATL 在類 IObjectSafetyImpl 中提供了此接口的實作。如果浏覽器發現你的控件支援 IObjectSafety,就在導入控件之前調用 IObjectSafety::SetInterfaceSafetyOptions 方法來確定安全性腳本操作。就不會彈出提示對話框。
在USBKey.h檔案裡繼承類清單的末尾(class ATL_NO_VTABLE CUSBKey)加入如下語句: public IObjectSafetyImpl<CUSBKey, INTERFACESAFE_FOR_UNTRUSTED_CALLER| INTERFACESAFE_FOR_UNTRUSTED_DATA>,
并在COM 映射裡添加一下行(黑體部分):
BEGIN_COM_MAP(CUSBKey)
COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()
四、測試
在網頁裡通過腳本語言調用ActiveX控件DeanUSBKey。可以通過VBScript和JAVAScript來調用。為了能在腳本語言裡使用控件接口,需知道接口的classid,可以檢視接口的系統資料庫腳本檔案USBKey.rgs找到classid值。調用代碼為:
<object classid="clsid: 4F3320E4-4B66-4C85-8538-6E17699AAB46" id="Dean" name = "Dean" ></object>
<form id="form1" name="form1" method="post" action="">
<a href="#" onclick="return CallUSB();">js調用ActiveX測試</a>
<input id="Write" name="Write" type="button" value="vb調用ActiveX測試" />
</form>
<SCRIPT LANGUAGE="JavaScript">
<!—
//JavaScript調用Demo
function CallUSB()
{
try {
var USBContent = Dean.GetContent(0);
alert(USBContent);
} catch (e) {
alert("錯誤号: " + e.number );
}
return false;
}
//-->
</SCRIPT>
<script language="VBScript" type="text/vbscript">
‘VBScript調用Demo
Sub Write_OnClick
On Error Resume Next
USBContent = Dean.GetContent(0)
MsgBox USBContent
MsgBox (err.number and &hff)
End Sub
</script>
五、部署
Internet軟體分發機關是“軟體包”,它由包含.INF檔案或軟體分發.OSD檔案(或兩者都包括)的.CAB檔案所組成。一個分發機關也可以包含軟體元件,如ActiveX控件,DLL檔案等。
1、Inf檔案編寫
INF檔案是一個文本檔案,指定運作控件所需要下載下傳或者呈交的檔案(比如.DLL或者其它.OCX)。一個.INF檔案就捆綁了.CAB壓縮檔案所有的必須檔案。 預設情況下,與現有硬碟中檔案版本号相同的檔案不被下載下傳。INF檔案如下:
[version]
signature="$CHICAGO$"
AdvancedINF=2.0
[Add.Code]
FT_ND_API.dll=FT_ND_API.dll
DeanUSBKey.dll=DeanUSBKey.dll
[FT_ND_API.dll]
file-win32-x86=thiscab
DestDir=11
FileVersion=1,0,6,413
[DeanUSBKey.dll]
file-win32-x86=thiscab
RegisterServer=yes
clsid={4F3320E4-4B66-4C85-8538-6E17699AAB46}
DestDir=11
FileVersion=1,0,0,1
[RegisterFiles]
%11%\DeanUSBKey.dll
說明:
"thiscab" 是一個關鍵字,指包含該INF的CAB檔案。也可以從網上下載下傳所需要的DLL檔案,隻要指定一個HTTP 網址即可,如:
file-win32-x86=http://www.chengdujob.net/activex/DeanUSBKey.DLL
關鍵字"file-win32-x86"指定平台是x86。
"FileVersion"檔案版本号。
"DestDir"指的是裝載目錄或者檔案的位址: 11指系統目錄 WINDOWS/SYSTEM32;10 指Windows 目錄。
2、Cab打包
Windows在系統目錄中自帶了CAB制作工具IExpress(\WINDOWS\system32\目錄下)。打開IExpress:
1)選擇“Create new Self Extraction Directive file”,點選下一步。
2)選擇“Create compressed files only(ActiveX Installs)”,點選下一步。
3)點選Add,把檔案添加(ft_nd_api.dll,DeanUSBKey.dll, duk_usbkey.inf)添加進去,點選下一步。
4)點選Browse,輸入.CAB檔案的存放位址(包含所取檔案名),這裡取TestCAB.CAB,并且要選中 “Store files using Long File Name inside Package”。點選下一步。
5)選擇“Don’t save”,一直點選下一步,直到完成。
3、自動安裝