深入解析ATL第四章笔记

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把一系列操作封装起来简化了调用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值