我们在开发B/S项目时,往往会面临需要封装一些逻辑在客户端运行或者是需要提取一些客户端环境信息等方面的情况,基于微软技术我们一般会选择使用AcitveX技术,通过javascript调用ActiveX提供的对象来满足业务上的需求。不过,随着Windows XP SP2的到来,让问题变得更加复杂且难以处理。
首先,IE 7 默认是阻止非法签名的ActiveX控件安装。虽然可以通过降低IE安全级别来克服这一问题,但我们无法要求让所有的客户端都通过此方法来解决这一问题,特别是Internet用户。降低安全级别安装后的结果如下图:
其次,ActiveX控件的分发形式。常用的是CAB和独立安装包(EXE)。CAB文件包含.INF文件或软件分发.OSD文件,也可以包含其他软件组件,如 ActiveX 控件(.OCX)、.DLL文件、.EXE文件、Java类文件或小程序。当Web页上OBJECT元素的CODEBASE特性引用包含.INF文件的.CAB文件时,Internet Explorer将自动把.CAB文件作为软件分发单位下载并安装,每次访问时还会自动检测版本并进行更新。而INF文件则是一个文本文件,指定运行控件所需要下载或者呈交的文件(比如.DLL或者其它.OCX);EXE文件这儿就不多介绍了。值得我们注意的是,当我们开发的ActiveX控件无法正常在客户机安装时,前者带的问题会更大一些。例如,我们发布了一个基于Internet的B/S系统,用户通过IE访问系统时需要下载安装一个Active控件包(.CAB),当此控件包因为安全原因无法安装时,会带来网络流量问题。因为用户每次访问页面都需要下载这个包,当这个包比较大的时候,这个问题就更为突出。而EXE包只需要下载并安装一次。当然,这个是因为IE了非法签名ActiveX控件安装引起的,那么下面我们就谈谈如何让ActiveX控件在客户机中顺利安装。
要让ActiveX控件在客户机中顺利安装,需要对我们的CAB进行数字签名,这方面就不多说了,网上有很多这方面的资料,为了让用户可以信任我们的CAB包,我们需要经过授权的证书,在具体的项目中,我们选用了VeriSign Class3(相关信息)。当我们对CAB包签名之后发现客户端安装仍然有问题,虽然可以识别到我们的CAB包是有效的签名,但仍然提示是未经验证的,很多Windows XP SP2环境中仍然无法安装。究其原因,是因为我们的ActiveX控件没有实现ISafeObject接口,所以Windows XP SP2对我们的控件提出了警告。接下来的工作就比较简单了,加上对ISafeObject接口的实现就好了,然后对生成好的CAB包再进行签名。
以VB开发的ActiveX为例,实现ISafeObject接口的范例程序如下:
Code
' 模块中的定义部分
Option Explicit
Public Const IID_IDispatch = "{00020400-0000-0000-C000-000000000046}"
Public Const IID_IPersistStorage = _
"{0000010A-0000-0000-C000-000000000046}"
Public Const IID_IPersistStream = _
"{00000109-0000-0000-C000-000000000046}"
Public Const IID_IPersistPropertyBag = _
"{37D84F60-42CB-11CE-8135-00AA004BB851}"
Public Const INTERFACESAFE_FOR_UNTRUSTED_CALLER = &H1
Public Const INTERFACESAFE_FOR_UNTRUSTED_DATA = &H2
Public Const E_NOINTERFACE = &H80004002
Public Const E_FAIL = &H80004005
Public Const MAX_GUIDLEN = 40
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal ByteLen As Long)
Public Declare Function StringFromGUID2 Lib "ole32.dll" (rguid As _
Any, ByVal lpstrClsId As Long, ByVal cbMax As Integer) As Long
Public Type udtGUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Public m_fSafeForScripting As Boolean
Public m_fSafeForInitializing As Boolean
Sub Main()
m_fSafeForScripting = True
m_fSafeForInitializing = True
End Sub
' -----------------------分隔线-------------------------
' 类文件中的代码
Private Sub Class_Initialize()
m_fSafeForScripting = True
m_fSafeForInitializing = True
End Sub
Private Sub IObjectSafety_GetInterfaceSafetyOptions(ByVal riid As _
Long, pdwSupportedOptions As Long, pdwEnabledOptions As Long)
Dim Rc As Long
Dim rClsId As udtGUID
Dim IID As String
Dim bIID() As Byte
pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER Or _
INTERFACESAFE_FOR_UNTRUSTED_DATA
If (riid <> 0) Then
CopyMemory rClsId, ByVal riid, Len(rClsId)
bIID = String$(MAX_GUIDLEN, 0)
Rc = StringFromGUID2(rClsId, VarPtr(bIID(0)), MAX_GUIDLEN)
Rc = InStr(1, bIID, vbNullChar) - 1
IID = Left$(UCase(bIID), Rc)
Select Case IID
Case IID_IDispatch
pdwEnabledOptions = IIf(m_fSafeForScripting, _
INTERFACESAFE_FOR_UNTRUSTED_CALLER, 0)
Exit Sub
Case IID_IPersistStorage, IID_IPersistStream, _
IID_IPersistPropertyBag
pdwEnabledOptions = IIf(m_fSafeForInitializing, _
INTERFACESAFE_FOR_UNTRUSTED_DATA, 0)
Exit Sub
Case Else
Err.Raise E_NOINTERFACE
Exit Sub
End Select
End If
End Sub
Private Sub IObjectSafety_SetInterfaceSafetyOptions(ByVal riid As _
Long, ByVal dwOptionsSetMask As Long, ByVal dwEnabledOptions As Long)
Dim Rc As Long
Dim rClsId As udtGUID
Dim IID As String
Dim bIID() As Byte
If (riid <> 0) Then
CopyMemory rClsId, ByVal riid, Len(rClsId)
bIID = String$(MAX_GUIDLEN, 0)
Rc = StringFromGUID2(rClsId, VarPtr(bIID(0)), MAX_GUIDLEN)
Rc = InStr(1, bIID, vbNullChar) - 1
IID = Left$(UCase(bIID), Rc)
Select Case IID
Case IID_IDispatch
If ((dwEnabledOptions And dwOptionsSetMask) <> _
INTERFACESAFE_FOR_UNTRUSTED_CALLER) Then
Err.Raise E_FAIL
Exit Sub
Else
If Not m_fSafeForScripting Then
Err.Raise E_FAIL
End If
Exit Sub
End If
Case IID_IPersistStorage, IID_IPersistStream, _
IID_IPersistPropertyBag
If ((dwEnabledOptions And dwOptionsSetMask) <> _
INTERFACESAFE_FOR_UNTRUSTED_DATA) Then
Err.Raise E_FAIL
Exit Sub
Else
If Not m_fSafeForInitializing Then
Err.Raise E_FAIL
End If
Exit Sub
End If
Case Else
Err.Raise E_NOINTERFACE
Exit Sub
End Select
End If
End Sub
这时客户机上IE已经可以正常安装我们的CAB包了。结果如下图:
总结下来,Windows XP SP2对ActiveX的限制变得更加严格,对基于微软技术的B/S开发造成了相当大的困难,期待有更好的解决方案出现。
注:如果是VB开发的ActiveX,要实现ISafeObject接口需要使用objsafe.tlb,找了很久,附上这个类型库。