本篇介绍COM自动化,对脚本的支持。
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在浏览器中运行中会出现不安全控件的提示,下一篇提供解决方法。
源文件下载:BeginningCOM4.zip
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);
};
{
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在浏览器中运行中会出现不安全控件的提示,下一篇提供解决方法。
源文件下载:BeginningCOM4.zip
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?