使用COM对象的属性方法,都需要COM对象事先在IDL中定义出来,这样,一来,对经常需要增加属性方法的对象而言,就意味着需要经常修改IDL,而来,对于某些应用,属性是运行时才确定下来的,这就意味着,我们需要有一种方法来支持动态的增加COM对象的属性方法。
下面提供一种在JS中使用COM对象的动态属性方法的解决方案,当然C++中使用,可以通过对接口进行封装+操作符重载的方式来实现类似的效果。
在JS(脚本)中,访问一个COM对象,都必须是通过IDispatch接口。比如说,在JS中调用 obj.xxx 这样的属性/方法,JS先把obj查询出IDispatch接口,然后把xxx作为参数调用IDispatch::GetIDsOfNames换取属性xxx对应的DispId ,之后在把换回来的DispId作为参数调用IDispatch::Invoke执行 。
正常情况下,我们使用默认的IDispatch实现(IDispatchImpl),就能够完成在IDL中定义出来的属性方法的执行,但是对于需要执行一个在IDL中没有定义出来的属性方法时,我们就可以通过重载IDispatch的GetIDsOfNames和Invoke来实现了。
首先,我们必须为所有的动态属性分配一个DispId(不要和IDL中定义的冲突了),在GetIDsOfNames中,根据属性/方法名,返回对应的DispId,之后在Invoke中,根据DispId,执行对应的功能即可。
例如:
STDMETHODIMP CTestObj::GetIDsOfNames(
REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgdispid
)
{
REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgdispid
)
{
// 先从IDL中查找
HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::GetIDsOfNames(
HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::GetIDsOfNames(
riid, rgszNames, cNames, lcid, rgdispid);
if(DISP_E_UNKNOWNNAME == hr)
{
if(DISP_E_UNKNOWNNAME == hr)
{
// IDL中没有找到
hr = S_OK;
for(UINT i=0; i<cNames; i++)
{
if(DISPID_UNKNOWN == rgdispid[i])
{
hr = S_OK;
for(UINT i=0; i<cNames; i++)
{
if(DISPID_UNKNOWN == rgdispid[i])
{
// 判断是不是动态的属性方法
if(IsDynamicPropOrMethod(rgszNames[i]))
{
// 返回分配好的DispId .
rgdispid[i] = GetPropOrMethodDispId(rgszNames[i]);
}
else
{
hr = DISP_E_UNKNOWNNAME ;
}
}
}
}
return hr;
}
}
}
}
return hr;
}
// 返回值为VT_EMPTY,js中为undefined,标识这是一个不存在的方法
STDMETHODIMP CTestObj::Invoke(
STDMETHODIMP CTestObj::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr
)
{
{
// 执行动态属性/方法
if(IsDynamicDispId(dispidMember))
{
if(IsDynamicDispId(dispidMember))
{
// 执行对应的属性/方法
//pvarResult->vt = VT_I4 ;
//pvarResult->lVal = m_ePlayStatus ;
//pvarResult->vt = VT_I4 ;
//pvarResult->lVal = m_ePlayStatus ;
return InvokeDynamicPropOrMethod(...);
}
}
// 执行IDL的属性/方法
HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::Invoke(
HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::Invoke(
dispidMember, riid, lcid, wFlags, pdispparams,
pvarResult, pexcepinfo, puArgErr);
if( SUCCEEDED(hr)&&pvarResult&&VT_EMPTY==pvarResult->vt )
{
if( SUCCEEDED(hr)&&pvarResult&&VT_EMPTY==pvarResult->vt )
{
// 成功时,对没有返回值的方法,返回0,表示成功
pvarResult->vt = VT_I4;
pvarResult->vt = VT_I4;
pvarResult->lVal = 0;
}
if( FAILED(hr) && pvarResult && dispidMember >= 0 )
{
pvarResult->vt = VT_NULL; // 失败时,返回 null,表示失败
hr = 0 - hr; // 返回一个正值,使不产生异常
}
return hr;
}
}
if( FAILED(hr) && pvarResult && dispidMember >= 0 )
{
pvarResult->vt = VT_NULL; // 失败时,返回 null,表示失败
hr = 0 - hr; // 返回一个正值,使不产生异常
}
return hr;
}
转载于:https://blog.51cto.com/pyhcx/255405