1. 实现IDispatch接口
COM自动化是指实现了IDispatch接口,这样可以供VBScript,JScript能脚本调用。
实现IDispatch接口的对象,对应到VBScript和JScript中的Object。
IDispatch接口定义为:
方法数量不多,但很复杂,特别是Invoke方法,不过微软提供了一些辅助方法来简化实现过程。VARIANT在C/C++中的定义是一个结构体,比较复杂,具体内容可参考Windows SDK。
idl中提供了dispinterface关键字实现IDispatch接口,因为功能限制的原因不推荐使用,推荐的是实现双接口(Dual Interface),既可以通过用户定义的接口也可以通过IDispatch接口访问对象。
BeginningCOM示例中,IDL接口的定义:
dual标记该接口为双接口,id为成员的ID。
GetTypeInfoCount方法获取对象提供的类型信息的数量,如果对象提供类型信息,pCountTypeInfo设为1,否则为0。
GetTypeInfo获取类型信息,我们直接用API取的其信息返回即可。
取得类型信息作为类的成员变量:
GetTypeInfo的实现:
GetIDsOfNames函数获取成员的ID,上面取得了TypeInfo,那么调用DispGetIDsOfNames可根据成员名取得其ID,其实现为:
riid不使用,并且必须为IID_NULL。
Invoke函数用于调用类的成员函数。
2. 脚本语言调用COM组件
JS:
VBS:
HTML:
HTML在浏览器中运行中会出现不安全控件的提示,下一篇提供解决方法。
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);
};
{
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);
}
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;
}
{
* 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);
// ...
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;
}
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);
}
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);
}
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 ));
WScript.Echo(obj.Sum( 3 , 6 ));
VBS:
dim
obj
set obj = CreateObject ( " BeginningCOM.1 " )
msgbox (obj.Sum( 5 , 6 ))
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 >
var obj = new ActiveXObject( " BeginningCOM.1 " )
alert(obj.Sum( 1 , 2 ));
</ script >
HTML在浏览器中运行中会出现不安全控件的提示,下一篇提供解决方法。