一》》、通过ATL开发COM_1概述ATL 是为了减轻程序员开发COM的负担而提供的一套模板库 ATL提供的支持: 1. CComModule封装对组件的宿主支持,掩盖了(DLL和EXE)之间的差别。 2. 对IUnknown的支持 3. 对类工厂的支持 4. 自动化 : IDispatchImpl COM 数据类型 : CComBSTR, CComVariant 接口指针 : CComPtr, CComQIPtr 错误处理 :ISupportErrorInfoImpl, CComObject 连接点 : IConnectionPointContainerImpl, IConnectionPointImpl :支持服务器的回叫信号(callback)或事件(event) 异步属性下载 : CBindStatusCallback 自注册 : 视窗和对话框 : Cwindow, CwindowImpl, CDialogImpl, CmessageMap 每个COM组件需要实现这三个功能 1. 实现IUnknown接口 (通过 CComObjectRootEx 实现) 2. 实现一个类工厂,支持组件的创建 (通过 CComCoClass 实现) 3. 实现自注册 一个典型ATL COM组件的定义
- class ATL_NO_VTABLE CAtlMath :
- public CComObjectRootEx<CComSingleThreadModel>,
- public CComCoClass<CAtlMath, &CLSID_AtlMath>,
- public IDispatchImpl<IAtlMath, &IID_IAtlMath, &LIBID_ATL_ServerLib, 1, 0>
- {
- }
CComObjectRootEx 提供 IUnknown支持 CComCoClass 提供类工厂的支持
每个COM组件需要实现这三个功能 1. 实现IUnknown接口 (通过 CComObjectRootEx 实现) 2. 实现一个类工厂,支持组件的创建 (通过 CComCoClass 实现) 3. 实现自注册 ATL是如何实现IUnknown接口的? 组件通过派生CComObjectRootEx 获得 IUnknown支持
- template <class ThreadModel>
- class CComObjectRootEx : public CComObjectRootBase
- {
- typedef ThreadModel _ThreadModel;
- ULONG InternalAddRef()
- {
- return _ThreadModel::Increment(&m_dwRef);
- }
- ULONG InternalRelease()
- {
- return _ThreadModel::Decrement(&m_dwRef);
- }
- };
- class CComObjectRootBase
- {
- static HRESULT WINAPI InternalQueryInterface(void* pThis,
- const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
- {
- HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
- return _ATLDUMPIID(iid, pszClassName, hRes);
- }
- 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 提供集合的IUnknown方法支持 :OuterAddRef 和 OuterRelease CComObjectRootEx 提供非集合的IUnknown方法支持 :InternalAddRef 和 InternalRelease 另外CComObjectRootBase还提供 IUnknown的QueryInterface方法支持 :InternalQueryInterface 和 OuterQueryInterface
IUnknown方法最终通过 CComObject 对用户开放:
- template <class Base>
- class CComObject : public Base
- {
- 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);
- }
- };
最终我们可以这样声明和使用ATL COM组件
- class ATL_NO_VTABLE CAtlMath :
- public CComObjectRootEx<CComSingleThreadModel>,
- public CComCoClass<CAtlMath, &CLSID_AtlMath>,
- public IDispatchImpl<IAtlMath, &IID_IAtlMath, &LIBID_ATL_ServerLib, 1, 0>
- {
- }
- IAtlMath *pIMath = new CComObject <CAtlMath>;
------------------------------------------------------------------------------------------------------------- 其实共有9个类似 CComObject 的类用于处理对用户开放的IUnknown接口,创建可实例化的COM组件。 CComObject : 不支持集合,不能处理引用计数 CComObjectNoLock : 同CComObject, 且该对象生命周期不影响组件宿主文件的锁定计数。ATL在宿主类工厂实现中使用该类 CComAggObject : 支持集合 CComContainedObject : 类似一个集合对象 CComPolyObject 支持集合和标准实现,是一个占位实现 CComObjectStack :完全不支持引用计数,结合栈创建COM对象一起使用 CComObjectGlobal : 生存期与组件宿主文件生存期相同,类似全局C++对象 CComObjectCached :存储在对象的缓存里,ATL用它实现组件的类工厂 CComTearOffObject :只在需要时创建类
------------------------------------------------------------------------------------------------------------- 总结一下 IUnknown 的实现 1. 类厂会这样创建组件 CAtlMath IAtlMath *pIMath = new CComObject <CAtlMath>; //非集合
2. CComObject 提供了IUnknown 接口的实现,它会调用 CAtlMath 的非集合内部IUnknown接口
- template <class Base>
- class CComObject : public Base
- {
- STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
- STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease();
- STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw() { return _InternalQueryInterface(iid, ppvObject); }
- };
3. 组件 CAtlMath 会派生于 CComObjectRootEx, CComObjectRootEx提供了部分非集合IUnknown接口
- class ATL_NO_VTABLE CAtlMath :
- public CComObjectRootEx<CComSingleThreadModel>,
- {
- }
- template <class ThreadModel>
- class CComObjectRootEx : public CComObjectRootBase
- {
- ULONG InternalAddRef() { return _ThreadModel::Increment(&m_dwRef); }
- ULONG InternalRelease() { return _ThreadModel::Decrement(&m_dwRef); }
- };
4. CComObjectRootEx 派生于 CComObjectRootBase,CComObjectRootBase提供了部分非集合IUnknown接口和全部集合IUnknown接口
- class CComObjectRootBase
- {
-
- static HRESULT WINAPI InternalQueryInterface(void* pThis,
- const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
- {
- HRESULT hRes = AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
- return _ATLDUMPIID(iid, pszClassName, hRes);
- }
- 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;
- };
- };
- ATL_NO_VTABLE : __declspec (novtable)
告诉编译器不要为类生成或初始化一个Vtable结构,可让类的构造函数和析构函数更小,并减小内存使用
类工厂的实现,组件的创建过程 1. 在*_Server.cpp 中有
- BEGIN_OBJECT_MAP(ObjectMap)
- OBJECT_ENTRY(CLSID_Math, CMath)
- END_OBJECT_MAP()
展开后是这样:
- struct _ATL_OBJMAP_ENTRY30
- {
- const CLSID* pclsid;
- HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
- _ATL_CREATORFUNC* pfnGetClassObject;
- _ATL_CREATORFUNC* pfnCreateInstance;
- IUnknown* pCF;
- DWORD dwRegister;
- _ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
- _ATL_CATMAPFUNC* pfnGetCategoryMap;
- void (WINAPI *pfnObjectMain)(bool bStarting);
- };
- static ATL::_ATL_OBJMAP_ENTRY ObjectMap[] =
- {
- {
- &CLSID_Math,
- CMath::UpdateRegistry,
- CMath::_ClassFactoryCreatorClass::CreateInstance,
- CMath::_CreatorClass::CreateInstance,
- NULL,
- 0,
- CMath::GetObjectDescription,
- CMath::GetCategoryMap,
- CMath::ObjectMain
- },
- {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
- };
定义了一个_ATL_OBJMAP_ENTRY 类型的全局数组变量 ObjectMap 然后在 Dll 被载入时用这个数组初始化全局的 _Module 变量
- CComModule _Module;
- BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID )
- {
- if (dwReason == DLL_PROCESS_ATTACH)
- {
- _Module.Init(ObjectMap, hInstance);
- }
- }
- inline HRESULT CComModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE , const GUID* plibid) throw()
- {
- m_pObjMap = p;
- _ATL_OBJMAP_ENTRY* pEntry = m_pObjMap;
- while (pEntry->pclsid != NULL)
- {
- pEntry->pfnObjectMain(true);
- pEntry++;
- }
- }
当用户代码中调用 CoCreateInstance 时 HRESULT hr = CoCreateInstance( CLSID_Math, NULL, CLSCTX_INPROC, IID_IMath, (void**) &pMath );
Dll 中的 DllGetClassObject 会被调用,最终调用 CMath::_ClassFactoryCreatorClass::CreateInstance 创建CMath组件
- STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
- {
- return _Module.GetClassObject(rclsid, riid, ppv);
- }
- inline HRESULT CComModule::GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) throw()
- {
- _ATL_OBJMAP_ENTRY* pEntry = m_pObjMap;
- while (pEntry->pclsid != NULL)
- {
- if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))
- {
-
-
- pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (LPVOID*)&pEntry->pCF);
- pEntry->pCF->QueryInterface(riid, ppv);
- break;
- }
- pEntry++;
- }
- }
接下来看 CComCoClass 又是如何帮助实现 CMath::_ClassFactoryCreatorClass::CreateInstance 创建类厂的
- class ATL_NO_VTABLE CMath :
- public CComObjectRootEx<CComSingleThreadModel>,
- public CComCoClass<CMath, &CLSID_Math>,
-
- {
- }
- template <class T, const CLSID* pclsid = &CLSID_NULL>
- class CComCoClass
- {
- public:
-
-
- typedef ATL::CComCreator< ATL::CComObjectCached< ATL::CComClassFactory > > _ClassFactoryCreatorClass;
- typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComCreator< ATL::CComAggObject< x > > > _CreatorClass;
- typedef T _CoClass;
- template <class Q>
- static HRESULT CreateInstance(IUnknown* punkOuter, Q** pp)
- {
- return T::_CreatorClass::CreateInstance(punkOuter, __uuidof(Q), (void**) pp);
- }
- };
可见在 CComCoClass 中有两个typedef的定义, _ClassFactoryCreatorClass 用于创建类厂 而 _CreatorClass用于创建组件实例 DllGetClassObject 最终调用了其中的 _ClassFactoryCreatorClass 的 CreateInstance函数创建类厂
将 _ClassFactoryCreatorClass 展开
-
- template <class T1>
-
- class CComCreator
- {
- public:
- static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
- {
- *ppv = NULL;
- HRESULT hRes = E_OUTOFMEMORY;
- T1* p = new T1(pv));
- 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;
- }
- };
DllGetClassObject 调用
-
- CMath::_ClassFactoryCreatorClass::CreateInstance (CMath::_CreatorClass::CreateInstance, __uuidof(IUnknown), (LPVOID*)NULL);
然后在这里面创建了类厂 :ATL::CComObjectCached< ATL::CComClassFactory >* p = new ATL::CComObjectCached< ATL::CComClassFactory >(pv)); 最终获得类厂的接口 :p->QueryInterface(riid, ppv); 类厂类是ATL::CComClassFactory 它的定义
- typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid, LPVOID* ppv);
- class CComClassFactory :
- public IClassFactory,
- public CComObjectRootEx<CComGlobalsThreadModel>
- {
- public:
- BEGIN_COM_MAP(CComClassFactory)
- COM_INTERFACE_ENTRY(IClassFactory)
- END_COM_MAP()
-
- STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
- {
- HRESULT hRes = E_POINTER;
- *ppvObj = NULL;
- if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
- {
- ATLTRACE(atlTraceCOM, 0, _T("CComClassFactory: asked for non IUnknown interface while creating an aggregated object"));
- hRes = CLASS_E_NOAGGREGATION;
- }
- else
- hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
- return hRes;
- }
- void SetVoid(void* pv)
- {
- m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
- }
- _ATL_CREATORFUNC* m_pfnCreateInstance;
- };
这里面有趣的是 SetVoid(void* pv) 它将 CMath::_CreatorClass::CreateInstance 传给了类厂指针成员变量 _ATL_CREATORFUNC* m_pfnCreateInstance
然后,在调用类厂 CreateInstance 创建对象实例时, 它会在检查参数后,调用 CMath::_CreatorClass::CreateInstance 那再看看 CMath::_CreatorClass::CreateInstance 会是如何实现创建组件实例
- typedef ATL::CComCreator2< ATL::CComCreator< ATL::CComObject< x > >, ATL::CComCreator< ATL::CComAggObject< x > > > _CreatorClass;
- template <class T1, class T2>
- class CComCreator2
- {
- public:
- static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
- {
- ATLASSERT(ppv != NULL);
- return (pv == NULL) ?
- ATL::CComCreator< ATL::CComObject< CMath > >::CreateInstance(NULL, riid, ppv) :
- ATL::CComCreator< ATL::CComAggObject< CMath > >::CreateInstance(pv, riid, ppv);
- }
- };
它会通过pv参数判断组件是否组合,然后选择相应的创建模板类 ------------------------------------------------------------------------------------------------------------- 总结一下类厂和组件的创建过程 类厂创建过程: 1. 在*_Server.cpp 中定义CComModule _Module 全局变量和一个数组,里面填入组件创建的相关函数信息
- CComModule _Module;
- BEGIN_OBJECT_MAP(ObjectMap)
- OBJECT_ENTRY(CLSID_Math, CMath)
- END_OBJECT_MAP()
2. Dll 加载时完成_Module的初始化
- BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID )
- {
- if (dwReason == DLL_PROCESS_ATTACH)
- {
- _Module.Init(ObjectMap, hInstance);
- }
- }
- inline HRESULT CComModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE , const GUID* plibid) throw()
- {
- m_pObjMap = p;
-
- }
3. 用户调用 CoCreateInstance 时,dll导出函数 DllGetClassObject 会被调用
- STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
- {
- return _Module.GetClassObject(rclsid, riid, ppv);
- }
-
- inline HRESULT CComModule::GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) throw()
- {
- _ATL_OBJMAP_ENTRY* pEntry = m_pObjMap;
- while (pEntry->pclsid != NULL)
- {
- if ((pEntry->pfnGetClassObject != NULL) && InlineIsEqualGUID(rclsid, *pEntry->pclsid))
- {
-
-
- hr = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (LPVOID*)&pEntry->pCF);
- break;
- }
- pEntry++;
- }
- }
4. DllGetClassObject 会导致 CMath::_ClassFactoryCreatorClass::CreateInstance 的调用
- CMath::_ClassFactoryCreatorClass 类型是 CComCreator, 也就是 CComCreator::CreateInstance 被调用,创建了类厂
-
- template <class T1>
- class CComCreator
- {
- static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
- {
- *ppv = NULL;
- HRESULT hRes = E_OUTOFMEMORY;
- T1* p = new T1(pv));
- if (p != NULL)
- {
- p->SetVoid(pv);
- p->QueryInterface(riid, ppv);
-
- }
- }
- };
********************************************************************************* 组件的创建过程: 1. CComCreator::CreateInstance 创建类厂后,会调用 SetVoid(pv) 将组件创建的函数指针创给了类工厂
- class CComClassFactory :
- {
- void SetVoid(void* pv)
- {
- m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv;
- }
- _ATL_CREATORFUNC* m_pfnCreateInstance;
- };
2. 用户调用的 CoCreateInstance, 在成功创建类厂后,会调用类厂的 CreateInstance接口
- class CComClassFactory :
- {
- STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
- {
- HRESULT hRes = E_POINTER;
- *ppvObj = NULL;
- if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
- {
- ATLTRACE(atlTraceCOM, 0, _T("CComClassFactory: asked for non IUnknown interface while creating an aggregated object"));
- hRes = CLASS_E_NOAGGREGATION;
- }
- else
- hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
- return hRes;
- }
- _ATL_CREATORFUNC* m_pfnCreateInstance;
- };
3. 类厂的 CreateInstance 会调用通过 SetVoid 获取的组件创建的函数指针, 也就是CMath::_CreatorClass::CreateInstance
-
- template <class T1, class T2>
- class CComCreator2
- {
- public:
- static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
- {
- ATLASSERT(ppv != NULL);
- return (pv == NULL) ?
- ATL::CComCreator< ATL::CComObject< CMath > >::CreateInstance(NULL, riid, ppv) :
- ATL::CComCreator< ATL::CComAggObject< CMath > >::CreateInstance(pv, riid, ppv);
- }
- };
4. CComCreator2 会通过检查pv参数,确定是创建组合对象还是非组合对象,最终还是通过 CComCreator 创建组件
-
- template <class T1>
- class CComCreator
- {
- static HRESULT WINAPI CreateInstance(void* pv, REFIID riid, LPVOID* ppv)
- {
- *ppv = NULL;
- HRESULT hRes = E_OUTOFMEMORY;
-
- T1* p = new T1(pv));
-
- hRes = p->QueryInterface(riid, ppv);
- return hRes;
- }
- };
到此,组件创建完成。
|