使用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
                )
{
        // 先从IDL中查找
        HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::GetIDsOfNames(
                                      riid, rgszNames, cNames, lcid, rgdispid);
         if(DISP_E_UNKNOWNNAME == hr)    
        {
                // IDL中没有找到
                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;
}
 
// 返回值为VT_EMPTY,js中为undefined,标识这是一个不存在的方法
STDMETHODIMP CTestObj::Invoke(
                  DISPID dispidMember,
                  REFIID riid,
                  LCID lcid,
                  WORD wFlags,
                  DISPPARAMS* pdispparams,
                  VARIANT* pvarResult,
                  EXCEPINFO* pexcepinfo,
                  UINT* puArgErr
                  )
    // 执行动态属性/方法
    if(IsDynamicDispId(dispidMember)) 
     {
        // 执行对应的属性/方法
        //pvarResult->vt = VT_I4 ; 
        //pvarResult->lVal = m_ePlayStatus ; 
        return InvokeDynamicPropOrMethod(...);
    } 
    // 执行IDL的属性/方法
    HRESULT hr = IDispatchImpl<TestObj, &IID_ITestObj>::Invoke(
                             dispidMember, riid, lcid, wFlags, pdispparams,
                             pvarResult, pexcepinfo, puArgErr);
    if( SUCCEEDED(hr)&&pvarResult&&VT_EMPTY==pvarResult->vt )
    {
        // 成功时,对没有返回值的方法,返回0,表示成功 
        pvarResult->vt = VT_I4; 
        pvarResult->lVal = 0;
    }
    if( FAILED(hr) && pvarResult && dispidMember >= 0 )
    {
        pvarResult->vt = VT_NULL;   // 失败时,返回 null,表示失败
        hr = 0 - hr;   // 返回一个正值,使不产生异常
    }
    return hr;
}