ATL接口映射宏详解[7]

七、COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid) 参ATL例程COMMAP


先看看这个宏的定义:
#define COM_INTERFACE_ENTRY_AUTOAGGREGATE(iid, punk, clsid)/
{&iid,/
(DWORD)&_CComCacheData</ CComAggregateCreator<_ComMapClass, &clsid>,/
(DWORD)offsetof(_ComMapClass, punk)/
>::data,/
_Cache},
先看看它的典型用法:
class CAutoAgg :
public IDispatchImpl,
public ISupportErrorInfo,
public CComObjectRoot,
public CComCoClass
{
......
......
};
与一般的组件并无二样。
class COuter :
public CChainBase,
public IDispatchImpl,
public CComCoClass
{
BEGIN_COM_MAP(COuter)
COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IAutoAgg, m_pUnkAutoAgg.p,
CLSID_CAutoAgg)
END_COM_MAP()
CComPtr m_pUnkAutoAgg;
};
与宏COM_INTERFACE_ENTRY_AGGREGRATE(_)不同,COuter不用在FinalConstruct中创建聚
集组件。外部组件会自动创建聚集组件!!!
1。
template
_ATL_CACHEDATA _CComCacheData::data = {dwVar, Creator::Creat
eInstance};
2。
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD dw)
{
{
HRESULT hRes = E_NOINTERFACE;
_ATL_CACHEDATA* pcd = (_ATL_CACHEDATA*)dw;
IUnknown** pp = (IUnknown**)((DWORD)pv + pcd->dwOffsetVar);
if (*pp == NULL)
hRes = pcd->pFunc(pv, IID_IUnknown, (void**)pp);
if (*pp != NULL)
hRes = (*pp)->QueryInterface(iid, ppvObject);
return hRes;
}
3。
template
class CComAggregateCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID/*riid*/, LPVOID* ppv
)
{
ATLASSERT(*ppv == NULL);
ATLASSERT(pv != NULL);
T* p = (T*) pv;
return CoCreateInstance(*pclsid, p->GetControllingUnknown(),
CLSCTX_INPROC, IID_IUnknown, ppv);
}
};
因为_Cache,_CComCacheData,CComAggregateCreator这几个类和函数我们已经在前面见
过或者见过类似的,所以就不再多讲了。总之我们可以看到,若m_pUnkAutoAgg.p不为空
则直接查询,否则创建聚集组件。
与宏COM_INTERFACE_ENTRY_AGGREGATE相比,这个宏似乎更好一些,仅当需要时才会创建
使用更简单。



八、COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND( punk, clsid ) 参ATL例程COMMAP

看看它的定义:
#define COM_INTERFACE_ENTRY_AUTOAGGREGATE_BLIND(punk, clsid)/
{NULL,/
(DWORD)&_CComCacheData</ CComAggregateCreator<_ComMapClass, &clsid>,/
(DWORD)offsetof(_ComMapClass, punk)/
>::data,/
_Cache},
呵呵,这个宏综合了COM_INTERFACE_ENTRY_AUTOAGGREGATE()和
COM_INTERFACE_ENTRY_AGGREGATE_BLIND()的特点,既可以自动创建也可以很方便地查询
聚集组件中的多个接口。不再赘述!!



九、COM_INTERFACE_ENTRY_CHAIN(classname) 参ATL例程COMMAP

先看看它的定义:
#define COM_INTERFACE_ENTRY_CHAIN(classname)/
{NULL,/
(DWORD)&_CComChainData::data,/
_Chain},

典型用法:
class CChain :
public IDispatchImpl,
public ISupportErrorInfo,
public CComObjectRoot
public CComCoClass
{
........
};
它与一般的组件无异。
class COuter :
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
_ATL_CHAINDATA _CComChainData::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()是什么效果大家自己试试吧,一定很熟悉的,呵呵。

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

---------------全文完------------

2000/3/31凌晨

补注:关于ATL中类厂的实现问题

1.当创建一个组件时,必须先创建它的类厂,再调用类厂的CreateInstance()来创建组件.
  在CComCoClass中定义了宏DECLARE_CLASSFACTORY(),包含了组件的类厂对象.
  _ClassFactoryCreatorClass,它的CreateInstance是用来创建组件的类厂的.也就是
  CComCreator < CCOMCLASSFACTORY>>::CreateInstance();
2.在CComCoClass中也定义了宏DECLARE_AGGREGATABLE(),包含了对象_CreatorClass,
  这个对象实际上就是我们要创建的组件对象(具体定义看详解一),它也有一个
  CreateInstance,这个函数是用来创建这个组件的!!当创建组件的类厂时,会把这
  个函数的地址告诉给类厂。
3.当我们成功的获得类厂对象后(此时类厂已经创建完毕),我们然后将调用类厂的
  CreateInstance(),在这个函数中,会调用组件的CreateInstance从而创建组件。
4.所以,可见这里总共牵扯到三个CreateInstance:
  (1)_ClassFactoryCreatorClass::CreateInstance()//用于创建组件的类厂对象
  (2)CComClassFactory::CreateInstance()//用于调用_CreatorClass::CreateInstance
  (3)_CreatorClass::CreateInstance()//用于创建组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值