脚本中访问COM组件,是通过组件暴露的IDispatch接口来实现的。但是,正常情况下在脚本中,只能访问组件默认的IDispatch接口,一旦组件实现了多个IDispatc接口,则无法访问非默认的接口。通过重载默认IDispatch接口的默认实现,可以来达到向脚本暴露非默认的IDispatch接口。
 
以下面的CTestObject为例:
class ATL_NO_VTABLE CTestObject:    
        ......
         public IDispatchImpl<IDefaultInterface, &IID_IDefaultInterface, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface1, &IID_IOtherInterface1, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface2, &IID_IOtherInterface2, &LIBID_TestLib,1,0>,
        ......
 
实现步骤如下:
 
一、实现中转的Dispatch Dummy类
    由于组件实现了多个IDispatch,如果直接重载IDispatch的API,将会是对所有接口的功能重写,这并不是我们希望的。所以通过添加一个中转的Dummy类来实现。
 
Dummy类实现:
template<
     class T,
     const IID* piid= &__uuidof(T),
     const GUID* plibid = &CAtlModule::m_libid,
     WORD wMajor = 1,
     WORD wMinor = 0,
     class tihclass = CComTypeInfoHolder    
>    
class CDispatchDummy
            : public IDispatchImpl<T,piid,plibid,wMajor,wMinor,tihclass>
{
public:
    STDMETHOD(GetIDsOfNamesDummy)(REFIID riid,LPOLESTR* rgszNames,
            UINT cNames,LCID lcid,DISPID* rgdispid)
    {
        return IDispatchImpl<T,piid,plibid,wMajor,wMinor,tihclass>::GetIDsOfNames(
                     riid,rgszNames,cNames,lcid,rgdispid);
    }

    STDMETHOD(InvokeDummy)(DISPID dispidMember, REFIID riid, LCID lcid,
            WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
            EXCEPINFO* pexcepinfo, UINT* puArgErr)
    {
        return IDispatchImpl<T,piid,plibid,wMajor,wMinor,tihclass>::Invoke(dispidMember,
              riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
    }

    STDMETHOD(GetIDsOfNames)(REFIID riid,LPOLESTR* rgszNames,UINT cNames,
            LCID lcid,DISPID* rgdispid)
    {
        return GetIDsOfNamesDummy(riid,rgszNames,cNames,lcid,rgdispid);
    }

    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
            DISPPARAMS* pdispparams, VARIANT* pvarResult,
            EXCEPINFO* pexcepinfo, UINT* puArgErr)
    {
        return InvokeDummy(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult,
                      pexcepinfo, puArgErr);
    }
};
 
 
二、修改默认的IDispatch接口的继承方式
class ATL_NO_VTABLE CTestObject:    
        ...... 
        // public IDispatchImpl<IDefaultInterface, &IID_IDefaultInterface, &LIBID_TestLib,1,0>, 
        public CDispatchDummy<IDefaultInterface, &IID_IDefaultInterface, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface1, &IID_IOtherInterface1, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface2, &IID_IOtherInterface2, &LIBID_TestLib,1,0>,
        ......
 
三、重载默认的IDispatch接口的实现
STDMETHOD(GetIDsOfNamesDummy)(REFIID riid,
     LPOLESTR* rgszNames,UINT cNames,LCID lcid,DISPID* rgdispid)
{
    // 先调用父类的实现,成功,直接返回
    // 如果调用失败,在这里,遍历其他非默认Dispatch接口的实现
    // 直到成功为止
    return S_OK;
 
STDMETHOD(InvokeDummy)(DISPID dispidMember, REFIID riid,
    LCID lcid, WORD wFlags, DISPPARAMS* pdispparams,
    VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
    // 先调用父类的实现,成功,直接返回
    // 如果调用失败,在这里,遍历其他非默认Dispatch接口的实现
    // 直到成功为止
    return S_OK;
 
四、完成
    这样,在脚本中,便能访问组件所有的IDispatch接口。对于组件来说,相当于把所有的IDispatch合并了!而在C++中,调用方式完全不变。
 
五、封装
    为了使用上方便,所以把所有的细节都包装成几个宏,存放于附件的DispatchDummy.h中。
 
使用:
#include “DispatchDummy.h”
 
class ATL_NO_VTABLE CTestObject:    
        ......
         public CDispatchDummy<IDefaultInterface, &IID_IDefaultInterface, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface1, &IID_IOtherInterface1, &LIBID_TestLib,1,0>,
         public IDispatchImpl<IOtherInterface2, &IID_IOtherInterface2, &LIBID_TestLib,1,0>,
        ......
{
        ......
 
typedef CDispatchDummy<IDefaultInterface, &IID_IDefaultInterface, &LIBID_TestLib,1,0> DefaultDisp ;
BEGIN_DISPATCH_INTERFACE_MAP(DefaultDisp)
    DISPATCH_INTERFACE_ENTRY(IOtherInterface1)
    DISPATCH_INTERFACE_ENTRY(IOtherInterface2)
END_XMP_INTERFACE_MAP()
       
        ......
}