class ATL_NO_VTABLE CFun :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CFun, &CLSID_Fun>,
public IFun
1:ATL简单对象基类简介
CComObjectRootBase提供了实现IUnknow的辅助函数,CComObjectRootEx从CComObjectRooBase派生并提供线程安全相关功能,IFun是自定义接口,CComCoClass定义了创建类厂对象的宏和创建com组建对象的宏
2:表驱动的QueryInterface
BEGIN_COM_MAP(CFun)
COM_INTERFACE_ENTRY(IFun)
END_COM_MAP()
宏展开如下
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),
{&_ATL_IIDOF(x), \
offsetofclass(x, _ComMapClass), \
_ATL_SIMPLEMAPENTRY},
__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;
我们的类(CFun)通过宏生成一个静态表,把每个接口的IID和偏移量插入到表中,以便外部通过代码IUnknown* pUnk = (IUnknown*)((INT_PTR)pThis+pEntries->dw);来获取相应的接口。_GetRawUnknown用来获取组建的IUnknow指针,因为所有接口都是从IUnknow派生,所以不能直接把组建的this指针转换成IUnknow指针,从上面可以看出来不能用直接使用我们的类(CFun),因为END_COM_MAP宏声明了3个纯虚函数却并有实现,可以按如下实现
STDMETHODIMP QueryInterface(REFIID iid,void **ppv)
{
return _InternalQueryInterface(iid,ppv);//_InternalQueryInterface在BEGIN_COM_MAP中定义
}
STDMETHODIMP AddRef()
{
return InternalAddRef(); //基类CComObjectRootEx中定义
}
STDMETHODIMP Release()
{
ULONG l = InternalRelease(); //基类CComObjectRootEx中定义
if (l == 0)
{
delete this;
}
return 1;
}
虽然这样可以实现,用户也可以直接生成CFun对象,但如此限制太多,比如CFun不能被聚合,所以才有了众多的CComXXX类。
3:CComObject
template <class Base>
class CComObject : public Base
{
public:
typedef Base _BaseClass;
...
CComObject(void* = NULL) throw()
{
_pAtlModule->Lock();
}
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw(){return _InternalQueryInterface(iid, ppvObject);}template <class Q>HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) throw(){return QueryInterface(__uuidof(Q), (void**)pp);}static HRESULT WINAPI CreateInstance(CComObject<Base>** pp) throw();};
CComObject用来创建非聚合对象,从我们的类派生,其QueryInterface可以方便我们更好的查询使用
4:CComAggObject
template <class contained>
class CComAggObject :
public IUnknown,
public CComObjectRootEx< typename contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComAggObject(void* pv) : m_contained(pv)
{
_pAtlModule->Lock();
}
HRESULT _AtlInitialConstruct()
{
HRESULT hr = m_contained._AtlInitialConstruct();
if (SUCCEEDED(hr))
{
hr = CComObjectRootEx< typename contained::_ThreadModel::ThreadModelNoCS >::_AtlInitialConstruct();
}
return hr;
}
//If you get a message that this call is ambiguous then you need to
// override it in your class and call each base class' version of this
HRESULT FinalConstruct()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalConstruct();
return m_contained.FinalConstruct();
}
void FinalRelease()
{
CComObjectRootEx<contained::_ThreadModel::ThreadModelNoCS>::FinalRelease();
m_contained.FinalRelease();
}
// Set refcount to -(LONG_MAX/2) to protect destruction and
// also catch mismatched Release in debug builds
virtual ~CComAggObject()
{
m_dwRef = -(LONG_MAX/2);
FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(this);
#endif
_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)
{
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;
}
template <class Q>
HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)
{
return QueryInterface(__uuidof(Q), (void**)pp);
}
static HRESULT WINAPI CreateInstance(LPUNKNOWN pUnkOuter, CComAggObject<contained>** pp)
{
ATLASSERT(pp != NULL);
if (pp == NULL)
return E_POINTER;
*pp = NULL;
HRESULT hRes = E_OUTOFMEMORY;
CComAggObject<contained>* p = NULL;
ATLTRY(p = new CComAggObject<contained>(pUnkOuter))
if (p != NULL)
{
p->SetVoid(NULL);
p->InternalFinalConstructAddRef();
hRes = p->_AtlInitialConstruct();
if (SUCCEEDED(hRes))
hRes = p->FinalConstruct();
if (SUCCEEDED(hRes))
hRes = p->_AtlFinalConstruct();
p->InternalFinalConstructRelease();
if (hRes != S_OK)
{
delete p;
p = NULL;
}
}
*pp = p;
return hRes;
}
CComContainedObject<contained> m_contained;
};
CComAggObject运用了继承和组合来实现聚合激活,因此有2个CComObjectRootBase实例,从CComObjectRootEx的负责维护对象的生命周期和接口查询,使用联合的成员m_dwRef,从CFun派生的负责转发给外部调用,使用联合成员m_pOuterUnknown
5:CComPolyObject
template <class contained>
class CComPolyObject :
public IUnknown,
public CComObjectRootEx< typename contained::_ThreadModel::ThreadModelNoCS >
{
public:
typedef contained _BaseClass;
CComPolyObject(void* pv) : m_contained(pv ? pv : this)
{
_pAtlModule->Lock();
}
...
}
CComPolyObject根据参数来决定是否聚合,若不聚合则表现为CComObject,否则CComAggObject
6:CComCreator
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;
#pragma warning(push)
#pragma warning(disable: 6014)
/* prefast noise VSW 489981 */
ATLTRY(p = new T1(pv))
#pragma warning(pop)
if (p != NULL)
{
p->SetVoid(pv);
p->InternalFinalConstructAddRef();
hRes = p->_AtlInitialConstruct();
if (SUCCEEDED(hRes))
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;
}
};
CComCreator是创建一个独立或者聚合实例的创建者类,其参数是正在创建的C++类,例如CComObject<CFun>,使用CComCreator可以简化我们创建实例的代码。有时候我们需要多步构造,因为对象的一些初始化操作有时不能放在构造函数里,如虚函数,返回值,异常,所以ATL提供了FinalConstruct,在FinalConstruct的实现中我们有可能把接口指针传给别人,别人用完接口指针后会release,导致对象提前析构,所以在调用FinalConstruct前应调用AddRef,如下所示
// CPenguin implementation
HRESULT CPenguin::FinalConstruct() {
HRESULT hr;
hr = CoCreateInstance(CLSID_EarthAtmosphere, 0, CLSCTX_ALL,
IID_IAir, (void**)&m_pAir);
if( SUCCEEDED(hr) ) {
// Pass reference to object with reference count of 0
hr = m_pAir->CheckSuitability(GetUnknown());
}
return hr;
}
// CEarthAtmosphere implementation in separate server
STDMETHODIMP CEarthAtmosphere::CheckSuitability(IUnknown* punk) {
IBreatheO2* pbo2 = 0;
HRESULT hr = E_FAIL;
// CPenguin's lifetime increased to 1 via QI
hr = punk->QueryInterface(IID_IBreatheO2, (void**)&pbo2);
if( SUCCEEDED(hr) ) {
pbo2->Release(); // During this call, lifetime decreases
// to 0 and destruction sequence begins...
}
return (SUCCEEDED(hr) ? S_OK : E_FAIL);
}
STDMETHODIMP
CPenguinCO::CreateInstance(IUnknown* pUnkOuter, REFIID riid,void** ppv)
{
*ppv = 0;
if( !pUnkOuter )
{
CComObject<CPenguin>* pobj = new CComObject<CPenguin>;
if( FAILED(hr) ) return E_OUTOFMEMORY;
// Protect object from pre-mature destruction
pobj->InternalAddRef();
hr = pobj->FinalConstruct();
pobj->InternalRelease();
if( SUCCEEDED(hr) ) ...
return hr;
}
...
}
调用InterAddRef有时候不是必要的,多线程不需要这种保护,因此有了InternalFinalConstructAddRef(CComObjectRootBase中定义,默认空实现,必要时可重载)
_AtlFinalConstruct,目的相同(错误处理),但用于ATL框架的的生命周期,其初始化早于FinalConstruct。
从上可以看到构造对象比较繁琐,因此CComCreator把一系列操作封装起来简化了调用。