码迷,mamicode.com
首页 > 其他好文 > 详细

Atl笔记二:BEGIN_COM_MAP

时间:2016-09-05 20:45:23      阅读:345      评论:0      收藏:0      [点我收藏+]

标签:

1,offsetofclass
获取基类相对于子类的偏移位置。

#define _ATL_PACKING 8

#define offsetofclass(base, derived) ((DWORD_PTR)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)

_ATL_PACKING非零就行,只是作为一个地址。因为为了避免虚类无法创建对象的问题所以没有通过类对象来计算。

技术分享


2,
//If you get a message that FinalConstruct is ambiguous then you need to
// override it in your class and call each base class‘ version of this
#define BEGIN_COM_MAP(x) public: \
????typedef x _ComMapClass; \
????static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw) throw()\
????{\
????????_ComMapClass* p = (_ComMapClass*)pv;\
????????p->Lock();\
????????HRESULT hRes = E_FAIL; \
????????__try \
????????{ \
????????????hRes = ATL::CComObjectRootBase::_Cache(pv, iid, ppvObject, dw);\
????????} \
????????__finally \
????????{ \
????????????p->Unlock();\
????????} \
????????return hRes;\
????}\
????IUnknown* _GetRawUnknown() throw() \
????
{ ATLASSERT(_GetEntries()[0].pFunc == _ATL_SIMPLEMAPENTRY); return (IUnknown*)((INT_PTR)this+_GetEntries()->dw); } \
????_ATL_DECLARE_GET_UNKNOWN(x)\
????HRESULT _InternalQueryInterface(REFIID iid, void** ppvObject) throw() \
????{ return InternalQueryInterface(this, _GetEntries(), iid, ppvObject); } \
????const static ATL::_ATL_INTMAP_ENTRY* WINAPI _GetEntries() throw() { \
????
static const ATL::_ATL_INTMAP_ENTRY _entries[] = { DEBUG_QI_ENTRY(x)


struct _ATL_INTMAP_ENTRY
{
????const IID* piid; // the interface id (IID)
????DWORD_PTR dw;
????_ATL_CREATORARGFUNC* pFunc; //NULL:end, 1:offset, n:ptr
};


#define DEBUG_QI_ENTRY(x) \

????????{NULL, \
????????(DWORD_PTR)_T(#x), \
????????(ATL::_ATL_CREATORARGFUNC*)0},

typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv, REFIID riid, LPVOID* ppv, DWORD_PTR dw);


#define COM_INTERFACE_ENTRY(x)\
????{&_ATL_IIDOF(x), \
????offsetofclass(x, _ComMapClass), \
????
_ATL_SIMPLEMAPENTRY},

#define _ATL_IIDOF(x) __uuidof(x)
//__uuidof获取与x相关的GUID值

#define _ATL_SIMPLEMAPENTRY ((ATL::_ATL_CREATORARGFUNC*)1)

#define END_COM_MAP() \
????__if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }\
????{NULL, 0, 0}}; return &_entries[1];} \
????virtual ULONG STDMETHODCALLTYPE AddRef( void) throw() = 0; \
????virtual ULONG STDMETHODCALLTYPE Release( void) throw() = 0; \
????STDMETHOD(QueryInterface)(REFIID, void**) throw() = 0;

3,
static HRESULT WINAPI CComObjectRootBase ::InternalQueryInterface(void* pThis,
????????const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
????{
????????// Only Assert here. AtlInternalQueryInterface will return the correct HRESULT if ppvObject == NULL
#ifndef _ATL_OLEDB_CONFORMANCE_TESTS
????????ATLASSERT(ppvObject != NULL);
#endif
????????ATLASSERT(pThis != NULL);
????????// First entry in the com map should be a simple map entry
????????ATLASSERT(pEntries->pFunc == _ATL_SIMPLEMAPENTRY);
????#if defined(_ATL_DEBUG_INTERFACES) || defined(_ATL_DEBUG_QI)
????????LPCTSTR pszClassName = (LPCTSTR) pEntries[-1].dw;
????#endif // _ATL_DEBUG_INTERFACES
????????HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
????#ifdef _ATL_DEBUG_INTERFACES
????????_AtlDebugInterfacesModule.AddThunk((IUnknown**)ppvObject, pszClassName, iid);
????#endif // _ATL_DEBUG_INTERFACES
????????return _ATLDUMPIID(iid, pszClassName, hRes);
????}

4,
CComObjectRootBase中以表驱动的方式对接口的查询作了一个内部的实现,即InternalQueryInterface()。
所以在创建基于ATL的COM类时,需要创建一个包含所有实现接口的映射表。
ATL提供了BEGIN_COM_MAP、END_COM_MAP、COM_INTERFACE_ENTRY与COM_INTERFACE_ENTRY2这4个宏来创建接口映射表。
假设一个类CClassA继承了接口IIntA1和IIntA2,则该类的接口映射表创建如下:

class CClassA : public CComObjectRootEx<CComSingleThreadMode>
{
??? BEGIN_COM_MAP(CClassA)
??????? COM_INTERFACE_ENTRY(IIntA1)
??????? COM_INTERFACE_ENTRY(IIntA2)
??? END_COM_MAP()
??? ......
};

static const ATL::_ATL_INTMAP_ENTRY _entries[] =
{
{NULL, ????(DWORD_PTR)_T(#x), ????(ATL::_ATL_CREATORARGFUNC*)0},
{& __uuidof(IIntA1), offsetofclass(IIntA1, CClassA), (ATL::_ATL_CREATORARGFUNC*)1},
{& __uuidof(IIntA2), offsetofclass(IIntA2, CClassA), (ATL::_ATL_CREATORARGFUNC*)1},
{NULL, 0, 0}
};


而当CClassB继承了IIntB1IIntB2,并且IIntB1IIntB2都继承自IDispatch接口。
此时,如果客户程序在查询
IDispatch接口,QueryInterface所返回的IDispatch接口指针将无法确定其属于IIntB1还是IIntB2
在这种情况下,需要指定
IDispatch接口指针的默认指向。?COM_INTERFACE_ENTRY2()宏即是用于完成该功能。
下面代码将对
IDispatch接口的请求默认指向属于IIntB2IDispatch接口指针。
class CClassB : public CComObjectRootEx<CComSingleThreadMode>
{
??? BEGIN_COM_MAP(CClassB)
??????? COM_INTERFACE_ENTRY(IIntB1)
??????? COM_INTERFACE_ENTRY(IIntB2)
??????? COM_INTERFACE_ENTRY2(IDispatch, IIntB2)
??? END_COM_MAP()
??? ......
};


#define COM_INTERFACE_ENTRY2
(x, x2)\
????{&_ATL_IIDOF(x),\
????reinterpret_cast<DWORD_PTR>(static_cast<x*>(static_cast<x2*>(reinterpret_cast<_ComMapClass*>(8))))-8,\
????_ATL_SIMPLEMAPENTRY},

5,总结:
BEGIN_COM_MAP通过
一个静态的_GetEntries()方法,来获取在该方法中创建的一个静态COM接口映射表。

Atl笔记二:BEGIN_COM_MAP

标签:

原文地址:http://www.cnblogs.com/liuhan333/p/5843313.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!