用ATL开发和部署ActiveX网页控件

用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 所示。可以通过该对话框选择是否属性化和发布方式等。


图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 所示


图2 ATL 简单对象组件名字对话框


图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));

    if (FT_SUCCESS != retval)  

    {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);

    if (FT_SUCCESS != retval){return ReturnError(retval);}

    //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="#" οnclick="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 、自动安装

用浏览器调用ActiveX 组件或者发布组件打包文件都需要用OBJECT 元素。发布.CAB 文件,需要在OBJECT 元素的CODEBASE 特性引用包含.INF 文件的.CAB 文件。当访问该页面时,Internet Explorer 将自动把.CAB 文件作为软件分发单位下载并安装,每次访问时还会自动检测版本并进行更新。注意,浏览器出于安全性考虑,会拦截未经数字认证的控件。修改浏览器设置,在Internet 选项- 〉安全- 〉受信任的站点- 〉站点 中添加服务器地址,不要选复选框“对该区域中的所有站点要求服务器验证”。解决浏览器拦截问题,而不用更改浏览器的安全级别。

六、结束语

   程序在Windows Server 2003+Microsoft Visual Studio.NET2005(C++) 英文版环境下调试通过。组件技术得到越来越广泛的应用,而VC 提供的活动模板库为我们创建功能强大的COM 组件提供了很好的框架。通过ActiveX 网页控件使网页也可以访问客户端的硬件资源。丰富B/S 结构系统的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值