ATL是通过接口映射表来实现QueryInterface的。
1. 从CComObject, CComObjectCached, CComAggObject 等中可以看出,QueryInterface都是_InternalQueryInterface实现的。
template
<
class
Base
>
class
CComObject :
public
Base
{
public
:
...
//
if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
throw
()
{
return
_InternalQueryInterface(iid, ppvObject);}
...
};
template
<
class
contained
>
class
CComAggObject :
public
IUnknown,
public
CComObjectRootEx
<
contained::_ThreadModel::ThreadModelNoCS
>
{
public
:
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
{
ATLASSERT(ppvObject
!=
NULL);
if
(ppvObject
==
NULL)
return
E_POINTER;
*
ppvObject
=
NULL;
HRESULT hRes
=
S_OK;
if
(InlineIsEqualUnknown(iid))
{
*
ppvObject
=
(
void
*
)(IUnknown
*
)
this
;
AddRef();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.AddThunk((IUnknown
**
)ppvObject, (LPCTSTR)
contained::_GetEntries()[
-
1
].dw, iid);
#endif
//
_ATL_DEBUG_INTERFACES
}
else
hRes
=
m_contained._InternalQueryInterface(iid, ppvObject);
return
hRes;
}
}
2. 而_InternalQueryInterface 是定义于宏BEGIN_COM_MAP中的,并且调用了CComObjectRootBase中的InternalQueryInterface
。其中包含参数_GetEntries() ---这是接口映射表、
#define
BEGIN_COM_MAP(x) public:
.....
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)
3. InternalQueryInterface由调用AtlInternalQueryInterface来遍历口映射表来查找接口,并获得接口指针
static
HRESULT WINAPI 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。而接口映射表是通过一组宏来实现的
BEGIN_COM_MAP(CClass)
COM_INTERFACE_ENTRY(IA)
COM_INTERFACE_ENTRY(IB)
END_COM_MAP()
//
BEGIN_COM_MAP
#define
BEGIN_COM_MAP(x) public:
.....
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)
//
COM_INTERFACE_ENTRY
#define
COM_INTERFACE_ENTRY(x)
{
&
_ATL_IIDOF(x),
offsetofclass(x, _ComMapClass),
_ATL_SIMPLEMAPENTRY},
//
END_COM_MAP
#ifdef _ATL_DEBUG
#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
;
#else
#define
END_COM_MAP()
__if_exists(_GetAttrEntries) {{NULL, (DWORD_PTR)_GetAttrEntries, _ChainAttr }, }
{NULL,
0
,
0
}};
return
_entries;}
virtual
ULONG STDMETHODCALLTYPE AddRef(
void
)
throw
()
=
0
;
virtual
ULONG STDMETHODCALLTYPE Release(
void
)
throw
()
=
0
;
STDMETHOD(QueryInterface)(REFIID,
void
**
)
throw
()
=
0
;
#endif
//
_ATL_DEBUG
5. AtlInternalQueryInterface实现
ATLINLINE ATLAPI AtlInternalQueryInterface(
void
*
pThis,
const
_ATL_INTMAP_ENTRY
*
pEntries, REFIID iid,
void
**
ppvObject)
{
ATLASSERT(pThis
!=
NULL);
//
First entry in the com map should be a simple map entry
ATLASSERT(pEntries
->
pFunc
==
_ATL_SIMPLEMAPENTRY);
if
(ppvObject
==
NULL)
return
E_POINTER;
*
ppvObject
=
NULL;
if
(InlineIsEqualUnknown(iid))
//
use first interface
{
IUnknown
*
pUnk
=
(IUnknown
*
)((INT_PTR)pThis
+
pEntries
->
dw);
pUnk
->
AddRef();
*
ppvObject
=
pUnk;
return
S_OK;
}
while
(pEntries
->
pFunc
!=
NULL)
{
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;
}
else
//
actual function call
{
HRESULT hRes
=
pEntries
->
pFunc(pThis,
iid, ppvObject, pEntries
->
dw);
if
(hRes
==
S_OK ¦ ¦ (
!
bBlind
&&
FAILED(hRes)))
return
hRes;
}
}
pEntries
++
;
}
return
E_NOINTERFACE;
}
ATL实际是通过计算指针偏移量来获得指针的。
IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries-> dw);