ATL学习笔记(4): COM接口映射表

CComObjectRootObjectBase中以表驱动的方式对接口的查询作了一个内部的实现,即InternalQueryInterface()。所以在创建基于ATL的COM类时,需要创建一个包含所有实现接口的映射表。

1. BEGIN_COM_MAP、END_COM_MAP、COM_INTERFACE_ENTRY与COM_INTERFACE_ENTRY2宏

ATL提供了BEGIN_COM_MAP、END_COM_MAP、COM_INTERFACE_ENTRY与COM_INTERFACE_ENTRY2这4个宏来创建接口映射表。

假设一个类CClassA继承了接口IIntA和IIntB,则该类的接口映射表创建如下:

class CClassA : public CComObjectRootEx<CComSingleThreadMode>
{
    BEGIN_COM_MAP(CClassA)
        COM_INTERFACE_ENTRY(IIntA)
        COM_INTERFACE_ENTRY(IIntB)
    END_COM_MAP()
    ......
};

而当CClassB继承了IIntC和IIntD,并且IIntC和IIntD都继承自IDispatch接口。此时,如果客户程序在查询IDispatch接口,QueryInterface所返回的IDispatch接口指针将无法确定其属于IIntC还是IIntD。在这种情况下,需要指定IDispatch接口指针的默认指向。 COM_INTERFACE_ENTRY2()宏即是用于完成该功能。下面代码将对IDispatch接口的请求默认指向属于IIntD的IDispatch接口指针。

class CClassB : public CComObjectRootEx<CComSingleThreadMode>
{
    BEGIN_COM_MAP(CClassB)
        COM_INTERFACE_ENTRY(IIntC)
        COM_INTERFACE_ENTRY(IIntD)
        COM_INTERFACE_ENTRY2(IDispatch, IIntD)
    END_COM_MAP()
    ......
};

2. 接口映射表的实现与所提供的功能

将CClassB中的宏扩展可以得到以下代码并稍作精简得:
class CClassB : public CComObjectRootEx<CComSingleThreadMode>
{
// BEGIN_COM_MAP(CClassB)
public:
    typedef CClassB _ComMapClass;
    static HRESULT _Cache(void* pv, REFIID iid, void* ppvObject, DWORD_PTR dw)
    {
        _ComMapClass* p = (_ComMapClass*)pv;
        p->Lock();
        HRESULT hRes = E_FAIL;
        hRes = CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);
        p->Unlock();
        return hRes;
    }
    IUnknown* _GetRawUnknown()
    {  return (IUnknown*)((INT_PTR)this + _GetEntries()->dw); }
    HRESULT _InternalQueryInterface(REFIID iid, void** ppvObj)
    { return InternalQueryInterface(this, _GetEntries(), iid, ppvObj);  }
    const static ATL::_ATL_INTMAP_ENTRY* _GetEntries()
    { 
        static const ATL::_ATL_INTMAP_ENTRY _entries[] = 
        {
// COM_INTERFACE_ENTRY(IIntC)
            { &_ATL_IIDOF(IIntC), 
              offsetofclass(IIntC, _ComMapClass), 
              _ATL_SIMPLEMAPENTRY 
            },
// COM_INTERFACE_ENTRY(IIntD)
            { &_ATL_IIDOF(IIntD), 
              offsetofclass(IIntD, _ComMapClass), 
              _ATL_SIMPLEMAPENTRY 
            },
// COM_INTERFACE_ENTRY2(IDispatch, IIntD)
            { &_ATL_IIDOF(IIntD), 
              reinterpret_cast<DWORD_PTR>(static_cast<IDispatch*>(
              static_cast<IIntD*>(reinterpret_cast<_ComMapClass*>(8)))) - 8,
              _ATL_SIMPLEMAPENTRY 
            },
// END_COM_MAP()
            { NULL, 0, 0}
        };
        return &_entries;
    }
    virtual ULONG AddRef()  = 0;
    virtual ULONG Release() = 0;
    HRESULT QueryInterface(REFIID, void*) = 0;
};

根据上述代码,BEGIN_COM_MAP()等宏的作用,关键在于提供了一个静态的_GetEntries()方法,用于获取在该方法中创建的一个静态COM接口映射表。其中,当base类(或接口)是derived类(或接口)的父类(或接口)时,
offsetofclass(base,derived)宏用于返回base接口(或类)指针在类中相对derived指针的在派生类虚表中的指针偏移值。_ATL_SIMPLEMAPENTRY常量表示_ATL_INTMAP_ENTRY结构的第二个成员dw表示base与derived指针的偏移值。COM_INTERFACE_ENTRY2与COM_INTERFACE_ENTRY的主要区别即为此偏移值的计算。

同时,END_COM_MAP()宏还将IUnknown的AddRef()和Release()方法声明为纯虚函数。由此看出,IUnknown接口的方法将注定有该类的子类实现。

3. 结论

每个基于ATL的COM对象必须首先创建一个静态的接口映射表,这个表的创建工作由BEGIN_COM_MAP、END_COM_MAP、COM_INTERFACE_ENTRY与COM_INTERFACE_ENTRY2这四个宏来完成。它们创建了接口映射表和接口映射表的获取函数,同时还将IUnknown的方法声明为纯虚函数,它们将由其派生类实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值