2007年4月30日
COM对象实现接口的方法是多种多样的,常用的有多重继承、聚合、tear-off等。为了最大限度地支持接口查询的多样性,ATL使用表驱动的方式来管理接口查询,以做到代码封闭和扩展灵活,表条目格式为:
struct
_ATL_INTMAP_ENTRY
{
const
IID
*
piid;
DWORD_PTR dw;
_ATL_CREATORARGFUNC
*
pFunc;
};
piid是接口ID,dw为自定义参数,pFunc是自定义函数的地址。在QueryInterface时,ATL调用pFunc,并将dw传给pFunc,pFunc可以根据自己的需要解析dw,获得查询必需的信息。ATL预提供了一些函数来处理上述常用的查询请求,如_Creator、_Delegate等,它们被放置于CComObjectRootBase中。
(1)多重继承
ATL并没有提供支持多重继承的函数,而是将pFunc设置为_ATL_SIMPLEMAPENTRY宏,将dw设为父类子对象在子类对象中的偏移量,当搜索表时发现pFunc为_ATL_SIMPLEMAPENTRY时,便直接使用偏移量相加获得结果。
if
(pEntries
->
pFunc
==
_ATL_SIMPLEMAPENTRY)
{
IUnknown
*
pUnk
=
(IUnknown
*
)((INT_PTR)pThis
+
pEntries
->
dw);
pUnk
->
AddRef();
*
ppvObject
=
pUnk;
return
S_OK;
}
接口映射表项宏为:
#define
COM_INTERFACE_ENTRY(x)\
{
&
_ATL_IIDOF(x), \
offsetofclass(x, _ComMapClass), \
_ATL_SIMPLEMAPENTRY},
(2)聚合
ATL使用_Delegate函数来支持聚合,此时dw存储的是内部对象指针成员变量在外部对象中的偏移量,_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;
}
接口映射表项宏为:
#define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)\
{&iid,\
(DWORD_PTR)offsetof(_ComMapClass, punk),\
_Delegate},
(3)tear-off
ATL使用_Creator函数来支持tear-off,此时dw中存储的是一个静态_ATL_CREATORDATA结构的地址,_Creator利用其中的指向创建函数的指针创建一个tear-off对象。
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)
{
_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;
return pcd->pFunc(pv, iid, ppvObject);
}
接口映射表项宏为:
#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
(DWORD_PTR)&ATL::_CComCreatorData<\
ATL::CComInternalCreator< ATL::CComTearOffObject< x > >\
>::data,\
_Creator},
_ComCreatorData的相关定义如下:
struct _ATL_CREATORDATA
{
_ATL_CREATORFUNC* pFunc;
};
template <class Creator>
class _CComCreatorData
{
public:
static _ATL_CREATORDATA data;
};
template <class Creator>
_ATL_CREATORDATA _CComCreatorData<Creator>::data = {Creator::CreateInstance};
我们还可以定义自己的接口查询辅助函数和辅助数据,通过dw和pFunc来完成各种千奇百怪的接口实现方式,需要的只是想象力。
posted @
2007-04-30 11:37 michael 阅读(675) |
评论 (0) |
编辑 收藏
2007年4月10日
在COM中对象要通过class factory的接口(通常是IClassFactory)来创建。在ATL中,class factory也是从CComObjectRootEx派生的COM类,它跟普通的COM类一样,也通过CComObject或其同伴类将逻辑功能跟生存期管理分离。
在ATL中,class factory类和普通类对象的创建都可以通过被称为“创建者”的模板类来创建。
template <class T1>
class CComCreator
{
public:
static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
{
HRESULT hRes = E_OUTOFMEMORY;
T1* p = NULL;
p = new T1(pv);
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->FinalConstruct();
p->InternalFinalConstructRelease();
if (hRes == S_OK)
hRes = p->QueryInterface(riid, ppv);
if (hRes != S_OK)
delete p;
}
return hRes;
}
};
“创建者”通过提供一个静态函数CreateInstance来创建指定类的实例,并且查询指定的接口。第一个参数在实例化普通对象和class factory对象时会有不同:对于普通对象,通常会传递聚合外部对象的IUnknown指针,如果是非聚合创建,可以传递NULL(详见ATL对COM实体身份的支持四);稍后会介绍在class factory创建时第一个参数的用处。
在ATL中,一个class factory对象只能创建一种类型的COM类实例。在class factory的CreateInstance函数中,它不是硬编码被创建的对象类型,而是通过一个
函数指针成员变量来创建它所管理的对象,而这个函数指针指向的通常就是某个COM类的“创建者”类的CreateInstance静态函数的地址,此地址是在class factory对象被它自身的“创建者”的CreateInstance函数创建时通过第一个参数传进来的,class factory重新定义了SetVoid函数,并且保存这个地址:
class CComClassFactory :
public IClassFactory,
public CComObjectRootEx<CComGlobalsThreadModel>
{
public:
// IClassFactory
STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
HRESULT hRes = E_POINTER;
if (ppvObj != NULL)
{
*ppvObj = NULL;
if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
{
hRes = CLASS_E_NOAGGREGATION;
}
else
hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
}
return hRes;
}
STDMETHOD(LockServer)(BOOL fLock)
{
if (fLock)
_pAtlModule->Lock();
else
_pAtlModule->Unlock();
return S_OK;
}
void SetVoid(void* pv)
{
m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
}
_ATL_CREATORFUNC* m_pfnCreateInstance;
};
根据上面的讨论,一个COM类的创建者会像这个样子:
CComCreator< CComObject<CMyClass> >
而一个class factory的创建者会像这个样子:
CComCreator< CComCachedObject<CComClassFactory> >。
ATL在客户请求创建CMyClass对象时执行的伪代码如下:
typedef CComCreator< CComCachedObject<CComClassFactory> > CClassFactoryCreator;
typedef CComCreator< CComObject<CMyClass> > CMyClassCreator;
IClassFactory* pcf=NULL;
CClassFactoryCreator::CreateInstance(&CMyClassCreator::CreateInstance,__uuidof(IClassFactory),&pcf);
pcf->CreateInstance(pOuter,__uuidof(IMyInterface),ppvObj);
posted @
2007-04-10 17:36 michael 阅读(1528) |
评论 (0) |
编辑 收藏
2007年4月6日
有了CComObjectRootBase、CComObjectRootEx和线程模型特征类这些基础原料,CComObject和它的伙伴们便可以自由的发挥它们的想象了。
1.CComObject —— 普通堆对象
下面是精简的CComObject模板类的定义:
template
<
class
Base
>
class
CComObject :
public
Base
{
public
:
CComObject(
void
*
=
NULL)
throw
()
{
_pAtlModule
->
Lock();
}
virtual
~
CComObject()
throw
()
{
FinalRelease();
_pAtlModule
->
Unlock();
}
STDMETHOD_(ULONG, AddRef)() {
return
InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l
=
InternalRelease();
if
(l
==
0
)
delete
this
;
return
l;
}
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
throw
()
{
return
_InternalQueryInterface(iid, ppvObject);}
static
HRESULT WINAPI CreateInstance(CComObject
<
Base
>**
pp)
throw
();
};
“普通堆对象”是使用最为频繁的类,它有如下特征:
(1)在堆中分配内存 —— 计数归零时用delete销毁
(2)支持独立存在的对象 —— 使用InternalXXX来实现IUnknown功能
(3)产生时锁定服务器,销毁时解锁服务器 —— 适用于大多数COM对象
2. CComAggObject —— 被聚合堆对象
template
<
class
contained
>
class
CComAggObject :
public
IUnknown,
public
CComObjectRootEx
<
typename contained::_ThreadModel::ThreadModelNoCS
>
{
public
:
CComAggObject(
void
*
pv) : m_contained(pv)
{
_pAtlModule
->
Lock();
}
HRESULT FinalConstruct()
{
CComObjectRootEx
<
contained::_ThreadModel::ThreadModelNoCS
>
::FinalConstruct();
return
m_contained.FinalConstruct();
}
void
FinalRelease()
{
CComObjectRootEx
<
contained::_ThreadModel::ThreadModelNoCS
>
::FinalRelease();
m_contained.FinalRelease();
}
virtual
~
CComAggObject()
{
FinalRelease();
_pAtlModule
->
Unlock();
}
STDMETHOD_(ULONG, AddRef)() {
return
InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l
=
InternalRelease();
if
(l
==
0
)
delete
this
;
return
l;
}
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
{
HRESULT hRes
=
S_OK;
if
(InlineIsEqualUnknown(iid))
{
*
ppvObject
=
(
void
*
)(IUnknown
*
)
this
;
AddRef();
}
else
hRes
=
m_contained._InternalQueryInterface(iid, ppvObject);
return
hRes;
}
static
HRESULT WINAPI CreateInstance(LPUNKNOWN pUnkOuter, CComAggObject
<
contained
>**
pp);
CComContainedObject
<
contained
>
m_contained;
};
CComAggObject有如下特征:
(1)在堆中分配内存
(2)支持被聚合聚合的对象
实现了两份IUnknown:CComAggObject实现了内部聚合类真正的IUnknown,它管理对象的生存期,并且完成接口查询(通过contained对象的_InternalQueryInterface);contained对象实现了具有转发功能的IUnknown,它将所有的调用转发给CComAggObject在构造函数中收到的外部IUnknown指针。
(3)产生时锁定服务器,销毁时解锁服务器
3. CComTearOffObject —— tear-off 对象
template
<
class
Base
>
class
CComTearOffObject :
public
Base
{
public
:
CComTearOffObject(
void
*
pv)
{
m_pOwner
=
reinterpret_cast
<
Base::_OwnerClass
*>
(pv);
m_pOwner
->
AddRef();
}
~
CComTearOffObject()
{
FinalRelease();
m_pOwner
->
Release();
}
STDMETHOD_(ULONG, AddRef)()
throw
() {
return
InternalAddRef();}
STDMETHOD_(ULONG, Release)()
throw
()
{
ULONG l
=
InternalRelease();
if
(l
==
0
)
delete
this
;
return
l;
}
STDMETHOD(QueryInterface)(REFIID iid,
void
**
ppvObject)
throw
()
{
return
m_pOwner
->
QueryInterface(iid, ppvObject);
}
};
tear-off对象有如下特征:
(1)在堆中分配内存
(2)支持在ATL组件内部使用的tear-off技术
(3)生成时锁定父对象,销毁时解锁父对象
(4)生命周期由自己管理,接口查询委托父对象
4. CComObjectStack —— 栈对象
template <class Base>
class CComObjectStackEx : public Base
{
public:
CComObjectStackEx(void* = NULL)
{
m_hResFinalConstruct = FinalConstruct();
}
virtual ~CComObjectStackEx()
{
FinalRelease();
}
STDMETHOD_(ULONG, AddRef)()
{
return 0;
}
STDMETHOD_(ULONG, Release)()
{
return 0;
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{
return _InternalQueryInterface(iid, ppvObject);
}
HRESULT m_hResFinalConstruct;
};
栈对象有如下特征:
(1)内存分配于栈上 —— 不需要对象考虑释放问题
(2)无引用计数功能
(3)构造时初始化 —— 需要在构造函数调用后检查m_hResFinalConstruct,以判断对象是否构造成功
(4)不锁定服务器
5. CComObjectGlobal —— 全局对象
template <class Base>
class CComObjectGlobal : public Base
{
public:
CComObjectGlobal(void* = NULL)
{
m_hResFinalConstruct = FinalConstruct();
}
virtual ~CComObjectGlobal()
{
FinalRelease();
}
STDMETHOD_(ULONG, AddRef)() throw()
{
return _pAtlModule->Lock();
}
STDMETHOD_(ULONG, Release)() throw()
{
return _pAtlModule->Unlock();
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()
{
return _InternalQueryInterface(iid, ppvObject);
}
HRESULT m_hResFinalConstruct;
};
全局对象有如下特征:
(1)在全局数据区分配内存
(2)初始化时不锁定服务器,引用计数变化时才锁定或者解锁服务器,这使得全局对象可以被用于全局类厂对象,以解决服务器和类厂之间的死锁问题
(3)构造方式同栈对象
还有一些其它种类的生存期管理类,如CComObjectNoLock、CComObjectCached、CComPolyObject等等,它们也都有自己独到的用处,我们也可以根据自己的需要编写自定义的类。总之CComObjectRootBase、CComObjectRootEx和线程模型特征类就像是积木一样,我们可以任意的把它们组合成想要的形状。
posted @
2007-04-06 15:33 michael 阅读(3654) |
评论 (0) |
编辑 收藏
1.CComSingleThreadModel和CComMultiThreadModel特征类:
class CComSingleThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) throw() {return ++(*p);}
static ULONG WINAPI Decrement(LPLONG p) throw() {return --(*p);}
typedef CComFakeCriticalSection AutoCriticalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComSingleThreadModel ThreadModelNoCS;
};
class CComMultiThreadModel
{
public:
static ULONG WINAPI Increment(LPLONG p) throw() {return InterlockedIncrement(p);}
static ULONG WINAPI Decrement(LPLONG p) throw() {return InterlockedDecrement(p);}
typedef CComAutoCriticalSection AutoCriticalSection;
typedef CComCriticalSection CriticalSection;
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
};
其中的CFakeCriticalSection定义如下:
class CComFakeCriticalSection
{
public:
HRESULT Lock() throw() { return S_OK; }
HRESULT Unlock() throw() { return S_OK; }
HRESULT Init() throw() { return S_OK; }
HRESULT Term() throw() { return S_OK; }
};
这两个类提供了针对特定套间对象的同步控制支持。
CComSingleThreadModel适用于单线程套间对象,它不采用任何线程同步机制;CComMultiThreadModel适用于多线程套间对象,它使用win32的同步机制来实现。
2.CComObjectRootEx模板类
template <class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase
{
public:
typedef ThreadModel _ThreadModel;
typedef typename _ThreadModel::AutoCriticalSection _CritSec;
typedef CComObjectLockT<_ThreadModel> ObjectLock;
ULONG InternalAddRef()
{
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease()
{
return _ThreadModel::Decrement(&m_dwRef);
}
void Lock() {m_critsec.Lock();}
void Unlock() {m_critsec.Unlock();}
private:
_CritSec m_critsec;
};
它使用传入的线程模型特征类来实现对象独立存在时所需要的引用计数支持,同时提供了一个方便对象开发使用的同步锁,我们可以使用这个锁来完成对自定义数据访问的保护。
posted @
2007-04-06 14:39 michael 阅读(1608) |
评论 (0) |
编辑 收藏
下面是CComObjectRootBase的关键代码:
class
CComObjectRootBase
{
public
:
// 构造支持
//
HRESULT FinalConstruct();
void FinalRelease();
// 接口查询支持
//
static HRESULT WINAPI InternalQueryInterface(void* pThis,
const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject);
static HRESULT WINAPI _Break(void*, REFIID iid, void** , DWORD_PTR );
static HRESULT WINAPI _NoInterface(void* , REFIID , void** , DWORD_PTR );
static HRESULT WINAPI _Creator(void* pv, REFIID iid, void**ppvObject, DWORD_PTR dw);
static HRESULT WINAPI _Delegate(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw);
static HRESULT WINAPI _Chain(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw);
static HRESULT WINAPI _ChainAttr(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw);
static HRESULT WINAPI _Cache(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw);
// 聚合支持
//
ULONG OuterAddRef()
{
return m_pOuterUnknown->AddRef();
}
ULONG OuterRelease()
{
return m_pOuterUnknown->Release();
}
HRESULT OuterQueryInterface(REFIID iid, void ** ppvObject)
{
return m_pOuterUnknown->QueryInterface(iid, ppvObject);
}
// 实体身份支持
//
union
{
long m_dwRef;
IUnknown* m_pOuterUnknown;
};
};
CComObjectRootBase主要功能分为:
(1)构造支持:
即FinalConstruct和FinalRelease,它们用于安全的构造和销毁对象。
(2)接口查询支持
通过接口映射表来完成接口查询功能,详细内容见后面的文章。
(3)聚合状态下引用计数操作的支持
提供了将IUnknown请求委托给外部组件的支持。当对象独立存在时,对引用计数操作的支持被放置于CComObjectRootEx中,因为涉及到了套间和访问同步。
(4)实体身份支持
使得对象可以独立存在或者被聚合,因为运行时刻对象只能以一种方式存在,所以使用了union。
posted @
2007-04-06 13:51 michael 阅读(1502) |
评论 (0) |
编辑 收藏
一个COM对象必须实现IUnknown接口所规定的功能,即生命期管理和运行时类型转换。
(1)基本IUnknow功能
对象的生存方式可以是堆存储、栈存储以及全局存储,同时COM还提供了一种被聚合的生存方式。生存方式不同,AddRef、Release和QueryInterface的实现方式就不同。比如:对于堆方式,Release要在引用计数归零时负责释放堆空间,而栈以及全局方式则不必;对于聚合方式,AddRef、Release以及大部分的QueryInterface调用要委托给外部对象。
为了达到对象的逻辑功能与其生存方式的分离,COM把对IUnknown基本功能的实现推迟到类层次结构的最底层,即CComObject(或它的其他伙伴)处。
CComObjectRootBase为CComObject的实现提供原材料的支持,如提供引用计数变量、基本的接口查询机制等,它是一个“功能集中类”。
(2)套间安全支持
根据对象所处的套间类型,对象功能的实现需要不同的同步支持。对于单线程套间对象,由COM库来保证访问的同步;而对于多线程套间对象,需要自己来同步调用。为了使套间安全支持与逻辑功能分离,ATL提供了一组提供线程安全操作支持的特征类(traits类)—— CComSingleThreadModel及其伙伴。它们提供了对一个LONG变量“恰好线程安全”的增减操作,以及跟套间类型匹配的CriticalSection功能类。
ATL同时提供了CComObjectRootEx模板类,它以某种ThreadModel特征类作为参数,提供对引用计数“恰好线程安全”的操作,并且提供了一个用于对象同步的锁。
PS:“恰好线程安全”的操作的意思是:对于需要线程同步的对象,提供win32 CriticalSection支持;对于不需要的对象,则什么也不做。
posted @
2007-04-06 11:46 michael 阅读(1375) |
评论 (0) |
编辑 收藏