本篇介绍COM自动化,对脚本的支持。
1. 实现IDispatch接口
COM自动化是指实现了IDispatch接口,这样可以供VBScript,JScript能脚本调用。
实现IDispatch接口的对象,对应到VBScript和JScript中的Object。
IDispatch接口定义为:
interface
IDispatch : IUnknown
{
HRESULT GetTypeInfoCount(
[
out
] UINT
*
pCountTypeInfo);
HRESULT GetTypeInfo(
[
in
] UINT iTInfo,
[
in
] LCID lcid,
[
out
] ITypeInfo
**
ppTInfo);
HRESULT GetIDsOfNames(
[
in
] REFIID riid,
[
in
, size_is(cNames)] LPOLESTR
*
rgszNames,
[
in
] UINT cNames,
[
in
] LCID lcid,
[
out
, size_is(cNames)] DISPID
*
rgDispId);
HRESULT Invoke(
[
in
] DISPID dispIdMember,
[
in
] REFIID riid,
[
in
] LCID lcid,
[
in
] WORD wFlags,
[
in
,
out
] DISPPARAMS
*
pDispParams,
[
out
] VARIANT
*
pVarResult,
[
out
] EXCEPINFO
*
pExcepInfo,
[
out
] UINT
*
puArgErr);
};
方法数量不多,但很复杂,特别是Invoke方法,不过微软提供了一些辅助方法来简化实现过程。VARIANT在C/C++中的定义是一个结构体,比较复杂,具体内容可参考Windows SDK。
idl中提供了dispinterface关键字实现IDispatch接口,因为功能限制的原因不推荐使用,推荐的是实现双接口(Dual Interface),既可以通过用户定义的接口也可以通过IDispatch接口访问对象。
BeginningCOM示例中,IDL接口的定义:
[
object
, uuid(93C3840F
-
AD5A
-
4020
-
AAAB
-
313C4B61B184), dual]
interface
IBeginningCOM : IDispatch
{
[id(
1
)] HRESULT Sum([
in
]
int
a, [
in
]
int
b, [
out
, retval]
int
*
sum);
[id(
2
), propget] HRESULT Num([
out
, retval]
int
*
pVal);
[id(
2
), propput] HRESULT Num([
in
]
int
val);
}
dual标记该接口为双接口,id为成员的ID。
GetTypeInfoCount方法获取对象提供的类型信息的数量,如果对象提供类型信息,pCountTypeInfo设为1,否则为0。
STDMETHODIMP BeginningCOM::GetTypeInfoCount(UINT
*
pCountTypeInfo)
{
*
pCountTypeInfo
=
1
;
return
S_OK;
}
GetTypeInfo获取类型信息,我们直接用API取的其信息返回即可。
取得类型信息作为类的成员变量:
ITypeLib
*
pTypeLib;
HRESULT hr
=
S_OK;
hr
=
LoadRegTypeLib(LIBID_BEGINNINGCOMLib,
1
,
0
, LANG_NEUTRAL,
&
pTypeLib);
if
(SUCCEEDED(hr))
{
pTypeLib
->
GetTypeInfoOfGuid(IID_IBeginningCOM,
&
m_pTypeInfo);
//
...
GetTypeInfo的实现:
STDMETHODIMP BeginningCOM::GetTypeInfo(UINT iTypeInfo, LCID lcid,
ITypeInfo
**
ppITypeInfo)
{
if
(
*
ppITypeInfo
!=
NULL)
{
return
DISP_E_BADINDEX;
}
m_pTypeInfo
->
AddRef();
*
ppITypeInfo
=
m_pTypeInfo;
return
S_OK;
}
GetIDsOfNames函数获取成员的ID,上面取得了TypeInfo,那么调用DispGetIDsOfNames可根据成员名取得其ID,其实现为:
STDMETHODIMP BeginningCOM::GetIDsOfNames(REFIID riid,
LPOLESTR
*
rgszNames, UINT cNames, LCID lcid,
DISPID
*
rgDispId)
{
if
(riid
!=
IID_NULL)
{
return
DISP_E_UNKNOWNINTERFACE;
}
return
DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
}
riid不使用,并且必须为IID_NULL。
Invoke函数用于调用类的成员函数。
STDMETHODIMP BeginningCOM::Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS
*
pDispParams,
VARIANT
*
pVarResult, EXCEPINFO
*
pExcepInfo,
UINT
*
puArgErr)
{
if
(riid
!=
IID_NULL)
{
return
DISP_E_UNKNOWNINTERFACE;
}
return
DispInvoke(
this
, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}
2. 脚本语言调用COM组件
JS:
var
obj
=
new
ActiveXObject(
"
BeginningCOM.1
"
);
WScript.Echo(obj.Sum(
3
,
6
));
VBS:
dim
obj
set
obj
=
CreateObject
(
"
BeginningCOM.1
"
)
msgbox
(obj.Sum(
5
,
6
))
HTML:
<
script
type
="text/javascript"
>
var
obj
=
new
ActiveXObject(
"
BeginningCOM.1
"
)
alert(obj.Sum(
1
,
2
));
</
script
>
HTML在浏览器中运行中会出现不安全控件的提示,下一篇提供解决方法。