BEGIN_COM_MAP 「爱情、让人受尽委屈。」 2021-11-17 01:44 312阅读 0赞 1,offsetofclass 获取基类相对于子类的偏移位置。 \#define \_ATL\_PACKING 8 \#define offsetofclass(base, derived) ((DWORD\_PTR)(static\_cast<base\*>((derived\*)\_ATL\_PACKING))-\_ATL\_PACKING) \_ATL\_PACKING非零就行,只是作为一个地址。因为为了避免虚类无法创建对象的问题所以没有通过类对象来计算。 ![aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvNDg3NDc5LzIwMTYwOS80ODc0NzktMjAxNjA5MDYxOTE0MTIwMDQtMjgzNjc4MTA5LnBuZw][] 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; \\ 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) \{ ATLASSERT(pEntries->pFunc == \_ATL\_SIMPLEMAPENTRY); HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject); return \_ATLDUMPIID(iid, pszClassName, hRes); \} // QI support ATLINLINE ATLAPI AtlInternalQueryInterface( \_Inout\_ void\* pThis, \_In\_ const \_ATL\_INTMAP\_ENTRY\* pEntries, \_In\_ REFIID iid, \_COM\_Outptr\_ void\*\* ppvObject) \{ // First entry in the com map should be a simple map entry ATLASSERT(pEntries->pFunc == \_ATL\_SIMPLEMAPENTRY); if (InlineIsEqualUnknown(iid)) // use first interface \{ IUnknown\* pUnk = (IUnknown\*)((INT\_PTR)pThis+pEntries->dw); pUnk->AddRef(); \*ppvObject = pUnk; return S\_OK; \} HRESULT hRes; for (;; pEntries++) \{ if (pEntries->pFunc == NULL) \{ hRes = E\_NOINTERFACE; break; \} BOOL bBlind = (pEntries->piid == NULL); if (bBlind || InlineIsEqualGUID(\*(pEntries->piid), iid)) \{ if (pEntries->pFunc == \_ATL\_SIMPLEMAPENTRY) //offset \{ ATLASSERT(!bBlind); IUnknown\* pUnk = (IUnknown\*)((INT\_PTR)pThis+pEntries->dw); pUnk->AddRef(); \*ppvObject = pUnk; return S\_OK; \} // Actual function call hRes = pEntries->pFunc(pThis, iid, ppvObject, pEntries->dw); if (hRes == S\_OK) return S\_OK; if (!bBlind && FAILED(hRes)) break; \} \} \*ppvObject = NULL; return hRes; \} CComObjectRootBase的InternalQueryInterface内部调用AtlInternalQueryInterface遍历\_ATL\_INTMAP\_ENTRY数组(包含所有实现接口的映射表),找到基类的虚函数表返回。 (我原来用的VS2005,没有AtlInternalQueryInterface的实现,蛋疼了好久这个接口映射表的怎么返回的,我猜也是遍历但是没有看见源码一直心头惴惴不安。后面擦查看VS2013里面给出了AtlInternalQueryInterface的实现才把这口气舒出来。) 4, 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\} \}; ![aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvNDg3NDc5LzIwMTYwOS80ODc0NzktMjAxNjA5MDYxOTE0MTI1MzUtMTg2MTcxOTUyMC5wbmc][] 而当CClassB继承了IIntB1和IIntB2,并且IIntB1和IIntB2都继承自IDispatch接口。 此时,如果客户程序在查询IDispatch接口,QueryInterface所返回的IDispatch接口指针将无法确定其属于IIntB1还是IIntB2。 在这种情况下,需要指定IDispatch接口指针的默认指向。 COM\_INTERFACE\_ENTRY2()宏即是用于完成该功能。 下面代码将对IDispatch接口的请求默认指向属于IIntB2的IDispatch接口指针。 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接口映射表。 原文:[https://www.cnblogs.com/liuhan333/p/5843313.html][https_www.cnblogs.com_liuhan333_p_5843313.html] [aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvNDg3NDc5LzIwMTYwOS80ODc0NzktMjAxNjA5MDYxOTE0MTIwMDQtMjgzNjc4MTA5LnBuZw]: /images/20211116/f121483e43e94a8f84749875658ed32b.png [aHR0cHM6Ly9pbWFnZXMyMDE1LmNuYmxvZ3MuY29tL2Jsb2cvNDg3NDc5LzIwMTYwOS80ODc0NzktMjAxNjA5MDYxOTE0MTI1MzUtMTg2MTcxOTUyMC5wbmc]: /images/20211116/8da22149f0b644b9836b045d552b74de.png [https_www.cnblogs.com_liuhan333_p_5843313.html]: https://www.cnblogs.com/liuhan333/p/5843313.html
还没有评论,来说两句吧...