COM--类厂

来源:互联网 发布:python实现支付宝登录 编辑:程序博客网 时间:2024/05/17 07:59

什么是类厂

类厂就是COM类的工厂,确切的说是对象厂,是对象的生产基地,com库通过类厂创建com对象。每一个com类有个专门用于该com类的对象创建操作。它支持一个特殊的接口IClassFactory:
class IClassFactory:public IUnknown{  virtual HRESULT _stdcall CreateInstance(IUnknown * pUnknownOuter,const IID& idd,void**ppv)=0;  virtual HRESULT _stdcall LockServer(BOOL bLock)=0;};
其中CreateInstance 用于创建对应的COM对象。LockServer用于控制组件的生命周期。
每一个com对象有一个相应的类厂对象,如果一个组件程序实现了多个com对象,则相应有多个类厂。如图:

因为类厂本身也是一个对象,它被用于其他com对象的创建过程。类厂对象由DllGetClassObject引出函数创建。DllGetClassObject不是com库函数,而是由组件程序实现的引出函数,函数原型如下:
HRESULT DllGetClassObject(const CLSID& clsid,const IID& iid,(void**)ppv);
其中clsid是待创建对象的CLSID。iid和ppv分别用于指定接口IID和存放类厂接口指针。
COM库在接到对象创建的指令后,它要调用进程内组件的DllGetClassObject函数,由该函数创建类厂对象,并返回类厂对象的接口指针,com库或者客户一旦有了类厂对象的接口指针,他们就可以通过类厂接口IClassFactory的成员函数CreateInstance创建相应的COM对象。

COM库与类厂的交互

在COM库中有3个API用于对象的创建。CoGetClassObject   CoCreateInstance   CoCreateInstanceEx。通常情况下,客户程序调用其中之一完成对象的创建。并返回对象的初始接口指针。COM库与类厂也是通过这3个函数进行交互。
HRESULT CoGetClassObject(const CLSID& clsid,DWORD dwClsContext,COSERVERINFO*pServerInfo,const IID&iid,(void**)ppv);
CoGetClassObject 函数首先找到由clsid指定的COM类的类厂,然后连接到类厂对象,如果需要的,CoGetClassObject函数装入组件代码。
如果,COM对象是进程内组件对象的话,则CoGetClassObject调用Dll模块中的DllGetClassObject引出函数,把参数clsid、iid、ppv传给DllGetClassObject,由DllGetClassObject创建类厂,并返回类厂对象接口指针。

HRESULT CoCreateInstance(const CLSID& clsid,IUnknown * pUnknownOuter,DWORD dwClsContext,const IID&iid,(void**)ppv);
CoCreateInstance 是一个被包装过的辅助函数,在他的内部实际上也调用了CoGetClassObject ,其参数clsid,dwClsContext、idd、和ppv的含义与CoGetClassObject的函数一致,参数pUnknownOuter与类厂接口的CreateInstance对对应参数一致,用于对象聚合。CoCreateInstance 函数把通过类厂对象创建对象的过程封装起来,客户程序只需要指定对象类的CLSID和待输出的接口指针及接口ID,函数返回后,客户就可以得到对象的接口指针,客户程序可以不与类厂打交道,下面是CoGetClassObject的一种实现
HRESULT CoCreateInstance(const CLSID& clsid,IUnknown *pUnknownOuter,DWORD dwClsContext,const IID& iid,void **ppv){  IClassFactory *pCF; HRESULT hr; hr=CoGetClassObject(clsid,dwClsContext,NULL,IID_IClassFactory,(void*)pCF); if(FAILUE(hr))   return hr; hr=pCF->CreateInstance(pUnknownOuter,iid,(void*)ppv); pCF->Release(); return hr;}
CoCreateInstance函数首先利用CoGetClassObject函数创建类厂对象,然后用得到类厂对象的接口指针创建真正的com对象。最后把类厂对象释放掉并返回。
在COM对象创建过程中,客户程序、COM库和进程内的组件程序三者之间的顺序关系如下:
  1. CoCreateInstance调用CoGetClassObject函数;
  2. COM库找到DLL程序并进入进程;
  3. 调用DllGetClassObject函数;
  4. DllGetClassObject函数创建类厂;
  5. DllGetClassObject函数把类厂接口指针返回给CoGetClassObject函数;
  6. CoGetClassObject函数把类厂接口指针返回给CoCreateInstance;
  7. CoCreateInstance函数得到类厂后,调用类厂的对象创建函数;
  8. 类厂创建COM对象;
  9. 类厂把COM对象返回给CoCreateInstance函数,CoCreateInstance函数返回;
  10. 客户直接调用COM对象。

类厂的实现

由于类厂是一个支持IClassFactory接口的com对象,所以类厂的C++定义(以字典程序为例):

class CDictionaryFactory:public IClassFactory{protected:ULONG m_Ref;public:CDictionaryFactory(void);~CDictionaryFactory(void);//IUnknown membersHRESULT QureyInterface(const IID& idd,void **ppv);ULONG AddRef();ULONG Release();//IClassFactory memberHRESULT CreateInstance(IUnknown*,const IID& iid,void **ppv);HRESULT LockServer(BOOL);};
CDictionaryFactory的实现代码如下:

#include"stdafx.h"#include"factory.h"#include"dictcomp.h"extern ULONG g_LockNumber;extern ULONG g_DictionaryNumber;CDictionaryFactory:CDictionaryFactory(){m_Ref=0;}CDictionaryFactory::~CDictionaryFactory(){}HRESULT CDictionaryFactory::QueryInterface(const IID& iid,void ** ppv){if(iid==IID_IUnknown){*ppv=(IUnknown*)this;static_cast<IUnknow*>(*ppv)->AddRef();}else if (iid==IID_IClassFactory){*ppv=(IClassFactory*)this;static<IClassFactory*>(*ppv)->AddRef();}else{*ppv=NULL;return E_NOINSTANCE;}return S_OK;}ULONG CDictionaryFactory::AddRef(){m_Ref++;return (ULONG)m_Ref;}ULONG CDictionaryFactory::Release(){m_Ref--;if(m_Ref==0){delete this;return 0;}return (ULONG)m_Ref;}HRESULT CDictionaryFactory::CreateInstance(IUnknown* pUnknownOuter,const IID& iid,void ** ppv){CDictionary *pObj;HRESULT hr;*ppv=NULL;hr=E_OUTOFMEMORY;if(pUnknownOuter!=NULL){return CLASS_E_NOAGGREGATION; }//Create the object passing function to notify no destruction.pObj=new CDictionary();if(pObj==NULL){return hr;}hr=pObj->QueryInterface(iid,ppv);if(hr!=S_OK){g_Dictionary--;delete pObj;}return hr;}HRESULT CDictionaryFactory::LockServer(BOOL bLock){if(bLock)g_LockNumber++;elseg_LockNumber--;return NOERROR;}
引出函数DllGetClassObject的实现方法:
extern "C" HRESULT _stdcall DllGetClassObject(const CLSID& clsid,const IID& iid,void** ppv){if(clsid==CLSID_Dcitionary{CDictionaryFactory *pFactory=new CDictionaryFactory;if(pFactory==NULL){return E_OUTOFMEMORY;}HRESULT result=pFactory->QueryInterface(iid,ppv);return result;}else{return CLASS_E_CLASSNOTAVAILABLE;}}
函数首先判断CLSID是否为字典组件的clsid,如果是,则创建类厂对象并通过类厂对象的QureyInterface成员函数找到对应的接口,一般是IClassFactory接口,然后返回。

类厂对组件生存周期的控制

虽然类厂也是一个com对象,但我们通常只把它当做创建其他组件对象的手段。一般,客户程序或这com库只是在创建组件对象的时候才使用类厂对象的接口指针,创建完成后就把类厂对象丢弃掉,以后如果还要创建组件对象,可以再次获取类厂对象,所以,类厂对象并不被长久保存,只是在创建过程中被用到。
如果希望保留类厂的接口,在以后创建对象时继续使用,则需要一种机制锁定组件程序,即通过IClassFactory::LockServer来实现锁定或解锁。
0 0
原创粉丝点击