前面
两
篇文章分
别
介
绍
了
MFC ActiveX
应
用程序和使用
ATL
开发
ActiveX
的
简单实
例,但
还
有
两个问题
需要解
决
:
1)标记ActiveX控件为安全的控件 2)对控件进行数字签名。本文将结合这两点进行简单的介绍。
Building a Safe ActiveX Control
如何不想办法将控件标记为安全的,就会在Web页面与控件进行交互时出现如下图的警告信息:
下面将分别介绍在MFC ActiveX和ATL中如何标记一个控件为安全的控件。
要标记一个MFC ActiveX控件为安全,可以仿照下面代码修改而得:
//
CardScan.cpp : CCardScanApp 和DLL 注册的实现。
#include
"
stdafx.h
"
#include
"
CardScan.h
"
#include
"
comcat.h
"
#include
"
strsafe.h
"
#include
"
objsafe.h
"
CCardScanApp theApp;
const
GUID CDECL BASED_CODE _tlid
=
{
0x29959268
,
0x9729
,
0x458E
, {
0xA8
,
0x39
,
0xBB
,
0x39
,
0x2E
,
0xCB
,
0x7E
,
0x37
} };
const
WORD _wVerMajor
=
1
;
const
WORD _wVerMinor
=
0
;
const
CATID CLSID_SafeItem
=
{
0xB548F3C7
,
0x2135
,
0x4242
,{
0x92
,
0x0B
,
0xA7
,
0xBD
,
0xEE
,
0x6D
,
0x2B
,
0xA3
}};
//
{ 0x36299202, 0x9ef, 0x4abf,{ 0xad, 0xb9, 0x47, 0xc5, 0x99, 0xdb, 0xe7, 0x78}};
//
CCardScanApp::InitInstance - DLL 初始化
BOOL CCardScanApp::InitInstance()
{
BOOL bInit
=
COleControlModule::InitInstance();
if
(bInit)
{
}
return
bInit;
}
//
CCardScanApp::ExitInstance - DLL 终止
int
CCardScanApp::ExitInstance()
{
return
COleControlModule::ExitInstance();
}
HRESULT CreateComponentCategory(CATID catid, CHAR
*
catDescription)
{
ICatRegister
*
pcr
=
NULL ;
HRESULT hr
=
S_OK ;
hr
=
CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (
void
**
)
&
pcr);
if
(FAILED(hr))
return
hr;
//
Make sure the HKCR\Component Categories\{..catid}
//
key is registered.
CATEGORYINFO catinfo;
catinfo.catid
=
catid;
catinfo.lcid
=
0x0409
;
//
english
size_t len;
//
Make sure the provided description is not too long.
//
Only copy the first 127 characters if it is.
//
The second parameter of StringCchLength is the maximum
//
number of characters that may be read into catDescription.
//
There must be room for a NULL-terminator. The third parameter
//
contains the number of characters excluding the NULL-terminator.
hr
=
StringCchLength(catDescription, STRSAFE_MAX_CCH,
&
len);
if
(SUCCEEDED(hr))
{
if
(len
>
127
)
{
len
=
127
;
}
}
else
{
//
TODO: Write an error handler;
}
//
The second parameter of StringCchCopy is 128 because you need
//
room for a NULL-terminator.
hr
=
StringCchCopy(COLE2T(catinfo.szDescription), len
+
1
, catDescription);
//
Make sure the description is null terminated.
catinfo.szDescription[len
+
1
]
=
'
\0
'
;
hr
=
pcr
->
RegisterCategories(
1
,
&
catinfo);
pcr
->
Release();
return
hr;
}
//
HRESULT RegisterCLSIDInCategory -
//
Register your component categories information
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
//
Register your component categories information.
ICatRegister
*
pcr
=
NULL ;
HRESULT hr
=
S_OK ;
hr
=
CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (
void
**
)
&
pcr);
if
(SUCCEEDED(hr))
{
//
Register this category as being "implemented" by the class.
CATID rgcatid[
1
] ;
rgcatid[
0
]
=
catid;
hr
=
pcr
->
RegisterClassImplCategories(clsid,
1
, rgcatid);
}
if
(pcr
!=
NULL)
pcr
->
Release();
return
hr;
}
//
HRESULT UnRegisterCLSIDInCategory - Remove entries from the registry
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
ICatRegister
*
pcr
=
NULL ;
HRESULT hr
=
S_OK ;
hr
=
CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (
void
**
)
&
pcr);
if
(SUCCEEDED(hr))
{
//
Unregister this category as being "implemented" by the class.
CATID rgcatid[
1
] ;
rgcatid[
0
]
=
catid;
hr
=
pcr
->
UnRegisterClassImplCategories(clsid,
1
, rgcatid);
}
if
(pcr
!=
NULL)
pcr
->
Release();
return
hr;
}
//
DllRegisterServer - 将项添加到系统注册表
STDAPI DllRegisterServer(
void
)
{
HRESULT hr;
AFX_MANAGE_STATE(_afxModuleAddrThis);
if
(
!
AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return
ResultFromScode(SELFREG_E_TYPELIB);
if
(
!
COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return
ResultFromScode(SELFREG_E_CLASS);
//
Mark the control as safe for initializing.
hr
=
CreateComponentCategory(CATID_SafeForInitializing,
_T(
"
Controls safely initializable from persistent data!
"
));
if
(FAILED(hr))
return
hr;
hr
=
RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForInitializing);
if
(FAILED(hr))
return
hr;
//
Mark the control as safe for scripting.
hr
=
CreateComponentCategory(CATID_SafeForScripting,
_T(
"
Controls safely scriptable!
"
));
if
(FAILED(hr))
return
hr;
hr
=
RegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForScripting);
if
(FAILED(hr))
return
hr;
return
NOERROR;
}
//
DllUnregisterServer - 将项从系统注册表中移除
STDAPI DllUnregisterServer(
void
)
{
HRESULT hr;
AFX_MANAGE_STATE(_afxModuleAddrThis);
//
Remove entries from the registry.
hr
=
UnRegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForInitializing);
if
(FAILED(hr))
return
hr;
hr
=
UnRegisterCLSIDInCategory(CLSID_SafeItem,
CATID_SafeForScripting);
if
(FAILED(hr))
return
hr;
if
(
!
AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
return
ResultFromScode(SELFREG_E_TYPELIB);
if
(
!
COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return
ResultFromScode(SELFREG_E_CLASS);
return
NOERROR;
}
这里值得注意的一个地方是DllUnregisterServer函数,在这段代码中,我是将
hr
=
UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
hr
=
UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
这两句代码放在
if
(
!
AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))
return
ResultFromScode(SELFREG_E_TYPELIB);
if
(
!
COleObjectFactoryEx::UpdateRegistryAll(FALSE))
return
ResultFromScode(SELFREG_E_CLASS);
这两句代码的前面,如果你查阅MSDN,将会发现它上面的顺序和我是相反的,这应该是微软的一个错误代码,如果按照MSDN的代码来写,则你使用regsvr32 -u CardScan.ocx反注册时会报下面的错误:
调整为我所说的顺序就没问题了。
2)要标记使用ATL写的ActiveX控件为安全的控件,这比MFC要简单的多,只需要在控件头文件中增加几行代码就可以了:
class
ATL_NO_VTABLE CTestCtrl :
…
public
IObjectSafetyImpl
<
CTestCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER
|
INTERFACESAFE_FOR_UNTRUSTED_DATA
>
,
然后在COM映射表中增加一项:
BEGIN_COM_MAP(CTestCtrl)
…
COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()
Building a Signed ActiveX Control
ActiveX控件是个危险的东西,如果不对其合法性进行数字签名和验证,IE是会拒绝其安装的。
工具包准备:CABARC.exe, cert2spc.exe, makecab.exe, makecert.exe, signcode.exe(或新版本中的signtool),以上小工具都可以在VS的安装路径下"Common7"Tools"Bin找到,或去微软官方网站上下载。
ActiveX控件的安装过程中,一部分工作就是自注册,这需要控件在VERSIONINFO
结构中定义
OLESelfRegister值,你可以对资源文件进行编辑如下
BEGIN
BLOCK
"
StringFileInfo
"
BEGIN
BLOCK
"
080403a8
"
BEGIN
VALUE
"
CompanyName
"
,
"
TODO: <公司名>
"
VALUE
"
FileDescription
"
,
"
TODO: <文件说明>
"
VALUE
"
FileVersion
"
,
"
1.0.0.1
"
VALUE
"
InternalName
"
,
"
CardScan.ocx
"
VALUE
"
LegalCopyright
"
,
"
TODO: (C) <公司名>。保留所有权利。
"
VALUE
"
OLESelfRegister
"
,
"
\0
"
VALUE
"
OriginalFilename
"
,
"
CardScan.ocx
"
VALUE
"
ProductName
"
,
"
TODO: <产品名>
"
VALUE
"
ProductVersion
"
,
"
1.0.0.1
"
END
END
BLOCK
"
VarFileInfo
"
BEGIN
VALUE
"
Translation
"
,
0x804
,
936
END
END
打包为CAB文件
因为ActiveX控件要放在网站上供客户下载到本地,因此压缩是必需的。一段典型的html代码如下:
<
OBJECT
ID
="FuckATL1"
CODEBASE
="http://localhost:8080/CardScan.cab"
CLASSID
="CLSID:B548F3C7-2135-4242-920B-A7BDEE6D2BA3"
WIDTH
=300
HEIGHT
=200
/>
CODEBASE就指明了要下载的压缩包,其中包含了oxc,dll控件等所需要的文件。
通常CAB文件包含了一个INF文件,它用来描述CAB文件的所有细节信息,下面举个简单例子,代码如下:
;
Sample INF file
for
SCRIPTABLEACTIVEX
.
DLL
[version]
;
version signature
(
same
for
both NT and Win95
)
do
not
remove
signature
=
"
$CHICAGO$
"
AdvancedINF
=
2.0
[Add
.
Code]
CardScan
.
ocx
=
CardScan
.
ocx
CardScan
.
inf
=
CardScan
.
inf
[CardScan
.
ocx]
file-win32-x86
=
thiscab
clsid
=
{B548F3C7-
2135
-
4242
-920B-A7BDEE6D2BA3}
FileVersion
=
1
,
0
,
0
,
1
RegisterServer
=
yes
[CardScan
.
inf]
file
=
thiscab
;
end
of INF file
至于打包就不赘述了,详尽的图解过程请看《如何给ActiveX数字签名(Step by Step, Delphi)》