对于HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw(), 其中的
pUnkOuter决定所创造的组件是独立的还是聚合的。
CoCreateInstance
¦
¦
pUnkOuter == NULL -----yes-----> "new " CComObject <CClass>
¦
no
¦
"new " CComAggObject <CClass>
这里的 "new ",其实就是CComCreator.CreateInstance(pv, riid, ppv);
其中的pv = pUnkOuter ;
template
<
class
T1
>
class
CComCreator
{
public
:
static
HRESULT WINAPI CreateInstance(
void
*
pv, REFIID riid, LPVOID
*
ppv)
{
ATLASSERT(ppv
!=
NULL);
if
(ppv
==
NULL)
return
E_POINTER;
*
ppv
=
NULL;
HRESULT hRes
=
E_OUTOFMEMORY;
T1
*
p
=
NULL;
ATLTRY(p
=
new
T1(pv))
if
(p
!=
NULL)
{
p
->
SetVoid(pv);
p
->
InternalFinalConstructAddRef();
hRes
=
p
->
FinalConstruct();
if
(SUCCEEDED(hRes))
hRes
=
p
->
_AtlFinalConstruct();
p
->
InternalFinalConstructRelease();
if
(hRes
==
S_OK)
hRes
=
p
->
QueryInterface(riid, ppv);
if
(hRes
!=
S_OK)
delete p;
}
return
hRes;
}
};
这个过程对于独立或聚合来说,没有什么大的区别。
关于组件聚合,需要分两方面讨论:
1。 聚合别的组件(外部)
class
ATL_NO_VTABLE CComplexMath :
public
CComObjectRootEx
<
CComSingleThreadModel
>
,
public
CComCoClass
<
CComplexMath,
&
CLSID_ComplexMath
>
,
public
IDispatchImpl
<
IComplexMath,
&
IID_IComplexMath,
&
LIBID_COMPLEXCALCULATORLib
>
{
public
:
CComplexMath()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_COMPLEXMATH)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CComplexMath)
COM_INTERFACE_ENTRY(IComplexMath)
COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk)
<===
增加要暴露的内部组件接口,到内部组件IUnknown接口的映射
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
//
IComplexMath
public
:
STDMETHOD(Divide)(
/*
[in]
*/
int
x,
/*
[in]
*/
int
y,
/*
[out,retval]
*/
int
*
z );
//
Aggregation: Add the controlling macro, finalconstruct and final release
DECLARE_GET_CONTROLLING_UNKNOWN()
<===
获取外部IUnknown接口指针
IUnknown
*
ptrUnk;
<===
内部组件的IUnknown接口指针
HRESULT FinalConstruct()
{
return
CoCreateInstance(CLSID_SimpleMath, GetControllingUnknown(),CLSCTX_ALL, IID_IUnknown, (
void
**
)
&
ptrUnk);
<===
创建内部组件(引入pUnkOuter),并获得内部组件的IUnknown接口指针ptrUnk
}
void
FinalRelease()
{
ptrUnk
->
Release();
<===
释放内部组件的IUnknown接口指针
}
//
End
};
聚合接口映射宏:
COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk)
展开为:
#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)/
{&iid,/
(DWORD_PTR)offsetof(_ComMapClass, punk),/ // dw中存储的是内部对象接口指针在外部对象中的偏移量
_Delegate},
ATL使用_Delegate来支持聚合。
static
HRESULT WINAPI _Delegate(
void
*
pv, REFIID iid,
void
**
ppvObject, DWORD_PTR dw)
{
HRESULT hRes
=
E_NOINTERFACE;
IUnknown
*
p
=
*
(IUnknown
**
)((DWORD_PTR)pv
+
dw);
//
通过偏移量得到内部接口指针
if
(p
!=
NULL)
hRes
=
p
->
QueryInterface(iid, ppvObject);
//
查询得到所要求的内部接口指针
return
hRes;
}
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,
<==
使用_Delegate来支持聚合。
iid, ppvObject, pEntries
->
dw);
if
(hRes
==
S_OK ¦ ¦ (
!
bBlind
&&
FAILED(hRes)))
return
hRes;
}
}
pEntries
++
;
}
return
E_NOINTERFACE;
}
由此可见,接口映射表就像一个“路由器”---将不同的接口请求转发到外部或内部组件。
BEGIN_COM_MAP(CComplexMath)
COM_INTERFACE_ENTRY(IComplexMath)
COM_INTERFACE_ENTRY_AGGREGATE (IID_ISimpleMath, ptrUnk) <=== 增加要暴露的内部组件接口,到内部组件IUnknown接口的映射
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
2。被聚合的组件(内部)
一个组件以被聚合形式存在时,最关键在于---pUnkOuter 的引入(p = new T1(pv))。
被聚合的组件实现了两套:AddRef, Release 和 QueryInterface。
一套用于内部控制;
一套用于外部控制-- 通过pUnkOuter;
template
<
class
contained
>
class
CComAggObject :
public
IUnknown,
public
CComObjectRootEx
<
contained::_ThreadModel::ThreadModelNoCS
>
{
public
:
..............
STDMETHOD_(ULONG, AddRef)() {
return
InternalAddRef();}
//
内部
STDMETHOD_(ULONG, Release)()
//
内部
{
ULONG l
=
InternalRelease();
if
(l
==
0
)
delete
this
;
return
l;
}
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();
.........
}
else
hRes
=
m_contained._InternalQueryInterface(iid, ppvObject);
//
return
hRes;
}
...
CComContainedObject
<
contained
>
m_contained;
};
template
<
class
Base
>
//
Base must be derived from CComObjectRoot
class
CComContainedObject :
public
Base
{
public
:
typedef Base _BaseClass;
CComContainedObject(
void
*
pv) {m_pOuterUnknown
=
(IUnknown
*
)pv;}
#ifdef _ATL_DEBUG_INTERFACES
~
CComContainedObject()
{
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(m_pOuterUnknown);
}
#endif
STDMETHOD_(ULONG, AddRef)() {
return
OuterAddRef();}
//
外部
STDMETHOD_(ULONG, Release)() {
return
OuterRelease();}
//
外部
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
//
外部
{
return
OuterQueryInterface(iid, ppvObject);
}
template
<
class
Q
>
HRESULT STDMETHODCALLTYPE QueryInterface(Q
**
pp)
{
return
QueryInterface(__uuidof(Q), (
void
**
)pp);
}
//
GetControllingUnknown may be virtual if the Base class has declared
//
DECLARE_GET_CONTROLLING_UNKNOWN()
IUnknown
*
GetControllingUnknown()
{
#ifdef _ATL_DEBUG_INTERFACES
IUnknown
*
p;
_AtlDebugInterfacesModule.AddNonAddRefThunk(m_pOuterUnknown, _T(
"
CComContainedObject
"
),
&
p);
return
p;
#else
return
m_pOuterUnknown;
#endif
}
};
BEGIN_COM_MAP(CAdvance)
COM_INTERFACE_ENTRY(IAdvance)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMath1, m_pSimpleUnknown)----------
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMath2, m_pSimpleUnknown)------- 路由
END_COM_MAP() ¦
¦
¦
(内部被聚合类)
BEGIN_COM_MAP(CSimple)
COM_INTERFACE_ENTRY(IID_IMath1)
COM_INTERFACE_ENTRY(IID_IMath2)
END_COM_MAP()