ATL接口映射宏详解[8]

本文详细介绍了ATL中的COM_INTERFACE_ENTRY_CHAIN宏及其作用,它用于查询基类接口。此外,还列举并解释了COM_INTERFACE_ENTRY_IID、COM_INTERFACE_ENTRY2_IID、COM_INTERFACE_ENTRY_FUNC、COM_INTERFACE_ENTRY_FUNC_BLIND、COM_INTERFACE_ENTRY_NOINTERFACE和COM_INTERFACE_ENTRY_BREAK等宏的功能和用法,帮助理解ATL组件接口映射的机制。
九、COM_INTERFACE_ENTRY_CHAIN(classname) 参ATL例程COMMAP

  先看看它的定义:

#define COM_INTERFACE_ENTRY_CHAIN(classname)/
    {NULL,/
    (DWORD)&_CComChainData< classname, _ComMapClass >::data,/
    _Chain},

  典型用法:

class CChain :
    public IDispatchImpl< IChain, &IID_IChain, &LIBID_COMMAPLib >,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass< CChain,&CLSID_CChain >
{
    ........
};

  它与一般的组件无异。

class COuter :
    public CChain,
    ....
{
    BEGIN_COM_MAP(COuter)
        ......
        COM_INTERFACE_ENTRY_CHAIN(CChain)
    END_COM_MAP()
};

  我们对查询的过程已经很熟悉了,可以直接来看看_Chain的功能。_Chain()是CComObjectRootBase的成员函数:

static HRESULT WINAPI _Chain(void* pv, REFIID iid, void** ppvObject,DWORD dw)
{
    _ATL_CHAINDATA* pcd = (_ATL_CHAINDATA*)dw;
    void* p = (void*)((DWORD)pv + pcd->dwOffset);
    return InternalQueryInterface(p, pcd->pFunc(), iid, ppvObject);
}

struct _ATL_CHAINDATA
{
    DWORD dwOffset;
    const _ATL_INTMAP_ENTRY* (WINAPI *pFunc)();
};

  我们再看看宏定义中的dw部分:

template < class base, class derived >
_ATL_CHAINDATA _CComChainData< base, derived >::data =
    {offsetofclass(base, derived), base::_GetEntries};

  基本上我们已经看懂是怎么回事了,void *p将得到基类的指针,InteralQueryInterface 我们已经很熟悉了,_Chain把基类的指针以及基类的接口映射宏传给它,实际上是查询基类的接口!!!
  一般情况下把这个宏放在BEGIN_COM_MAP和END_COM_MAP之间的最后面,这表示只有在当前类中查不到接口时才去查父类的接口。不过也经常把它放在第一位,这时就是先去查父类接口,只有父类没有实现这种接口时才查自己。在ATL中组件是以多重继承的方式实现的,ATL定义了很多类实现了一些常用的接口,这些类经常被做为组件的基类,所以这个宏被大量使用。

  所有重要的宏我们都已经讲过了,剩下的都是些很简单的宏了.呵呵,还是把它们都罗列一下,善始善终嘛.

  十、COM_INTERFACE_ENTRY_IID(iid, x)

#define COM_INTERFACE_ENTRY_IID(iid, x)/
    {&iid,/
    offsetofclass(x, _ComMapClass),/
    _ATL_SIMPLEMAPENTRY},

  十一、COM_INTERFACE_ENTRY2_IID(iid, x, x2)

#define COM_INTERFACE_ENTRY2_IID(iid, x, x2)/
    {&iid,/
    (DWORD)((x*)(x2*)((_ComMapClass*)8))-8,/
    _ATL_SIMPLEMAPENTRY},

  从定义上看这两个宏与COM_INTERFACE_ENTRY()和COM_INTERFACE_ENTRY2()相比,都只是多了一项"iid"。没有别的好处,只不过由用户明确指出接口IID,而不用系统根据接口名字去转换了。

  十二、COM_INTERFACE_ENTRY_FUNC( iid, dw, func )

#define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)/
    {&iid, /
    dw, /
    func},

  还记得AtlInternalQueryInterface()中的代码吗?如果在接口映射表中找到了我们要找的接口,并且这个接口不是_ATL_SIMPLEENTRY型的,则执行宏定义中的指定的函数。
  这个宏就给我们提供了自己编写处理函数的功能。这个函数必须是如下定义:HRESULT WINAPI func(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
当AtlInternalQueryInterface调用func时,会传进相关的信息。pv是类对象的指针,riid是要查询的接口,ppv是要返回查询得到的接口指针,dw是在宏定义中指定的参数。另外如果函数中不打算返回接口指针,则应把ppv赋为NULL,并返回S_FALSE或 E_NOINTERFACE。返回S_FALSE刚会继续查找下去,若返回E_NOINTERFACE则会终止查询。若返回接口指针,则应返回S_OK.

  十三、COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)

#define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)/
    {NULL, /
    dw, /
    func},

  至于_BLIND类型的特点可以看前面几节。

  十四、COM_INTERFACE_ENTRY_NOINTERFACE(x)

#define COM_INTERFACE_ENTRY_NOINTERFACE(x)/
    {&_ATL_IIDOF(x), /
    NULL, /
    _NoInterface},

_NoInterface是CComObjectRootBase的成员函数,看看它的定义:
static HRESULT WINAPI _NoInterface(...)
{
    return E_NOINTERFACE;
}

  原来它只是返回E_NOINTERFACE,并且将终止查询。
  哈哈,看来是不想让别人查到这个接口啊!!!

  十五、COM_INTERFACE_ENTRY_BREAK(x)

#define COM_INTERFACE_ENTRY_BREAK(x)/
    {&_ATL_IIDOF(x), /
    NULL, /
    _Break},

_Break也是CComObjectRootBase的成员函数,看看它的定义:
static HRESULT WINAPI _Break(...)
{
    iid;
    _ATLDUMPIID(iid, _T("Break due to QI for interface "), S_OK);
    DebugBreak();
    return S_FALSE;
}

  如果查到这个接口将调用DebugBreak(),并返回S_FALSE,继续查询下去。DebugBreak()是什么效果大家自己试试吧,一定很熟悉的,呵呵。

  至此全部十五个接口映射宏我们都已经讲完了,唉,真是不容易,特别是前面几个宏跟踪起来很麻烦。因为文本方式的限制,所以很多东西不容易表达清楚。有些叫法也是我自己这么叫的,可能与别人的习惯不同。没办法,大家将就将就了,呵呵。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值