COM笔记-组件的创建和类厂

组件的创建和类厂

只针对进程内组件。
进程内组件一般使用DLL来实现,实现组件的DLL要导出以下四个函数:
DllGetClassObject
DllCanUnloadNow
DllRegisterServer
DllUnregisterServer

这些函数需要组件的实现者来实现并从DLL中导出。

++++++++++++++++++++++++++++++++++++++
组件的创建
函数DllGetClassObject和组件的创建紧密相关。在COM库中有三个函数可以创建对象(获得某个接口):
CoGetClassObject
CoCreateInstance
CoCreateInstanceEx
首先关注函数CoGetClassObject ,它的函数原型是:
STDAPI CoGetClassObject(
  REFCLSID rclsid,
  DWORD dwClsContext,
  COSERVERINFO * pServerInfo,
  REFIID riid,
  LPVOID * ppv
);

参数说明:
rclsid:class-id,指定了COM类的唯一标识。
dwClsContext:可执行代码的运行上下文。
pServerInfo:被用在DCOM中,一般设为NULL
riid: 通常情况下是接口IClassFactory的标识符IID_IClassFactor。如果类厂对象还支持IClassFactory2接口,也可以是接口IClassFactory2的标识符。
ppv:用来存放获得的接口指针。
对于进程内组件,CoGetClassObject 调用DLL中的导出(export)函数DllGetClassObject,并将参数rclsid,riid和ppv传递给函数DllGetClassObject。函数DllGetClassObject创建类厂对象,并将类厂对象接口通过参数ppv返回。

函数CoCreateInstance 是一个辅助函数,实际上该函数内部调用了CoGetClassObject 。该函数的参数rclsid,dwClsContext,riid,ppv含义与函数CoGetClassObject 的对应参数含义是一样。参数pUnkOuter与类厂接口的函数CreateInstance的相应参数是一致,主要被用在对象被聚合的情形。

STDAPI CoCreateInstance(
  REFCLSID rclsid,
  LPUNKNOWN pUnkOuter,
  DWORD dwClsContext,
  REFIID riid,
  LPVOID * ppv
);

函数CoCreateInstance将得到类厂,然后使用类厂得到接口指针的过程封装起来了。在参数ppv获得的接口指针就是riid对应的接口指针。函数CoCreateInstance的实现步骤一般是:
a 得到类厂接口的指针
b 创建COM对象,返回由riid标识的接口指针。

由于CoCreateInstance不能创建远程机器上的对象,所以COM库又提供了函数CoCreateInstanceEx。

HRESULT CoCreateInstanceEx(
  REFCLSID rclsid,
  IUnknown * punkOuter,
  DWORD dwClsCtx,
  COSERVERINFO * pServerInfo,
  ULONG cmq,
  MULTI_QI * pResults
);

前三个参数和CoCreateInstance的是一样的。参数pServerInfo指定了创建对象的机器信息,参数cmq指定了pResults指向的空间中MULTI_QI 的个数。参数pResults是MULTI_QI 结构数组。

在创建组件时,依据以下原则选择创建函数:
1)在远程机器上创建对象或一次得到多个接口时,使用函数CoCreateInstanceEx。
2)要操作类厂时,使用函数CoGetClassObject。
3)其他的情况使用函数CoCreateInstance。

CoCreateInstance的不灵活性
  当CoCreateInstance完成之后,组件实际上已经建立好了。在建立好一个组件之后,想要控制将组件装载到内存中何处或检查客户是否有权限来创建该组件基本上已经不可能了。为了控制一个组件的创建过程,我们需要另外一个组件(这个组件即类厂)来创建所需的组件。

++++++++++++++++++++++++++++++++++++++
类厂(class factory)

   类厂组件的唯一功能就是创建其他的组件,某个特定的类厂将创建只同某个特定的CLSID相对应的组件。
   CoCreateInstance实际上并没有直接创建COM组件,而是创建了一个被称作是类厂的组件。所需的组件正是由此类厂创建的。创建组件的标准接口是IClassFactory,用
CoCreateInstance创建的组件实际上是通过IClassFactory来创建所需的组件的。
使用类厂创建组件分两步:创建类厂本身;在使用一个接口来创建所需的组件。
CoGetClassObject返回的是指向所需组件的类厂而不是指向组件本身的一个指针。客户可以用CoGetClassObject所返回的指针来创建所需的组件。这个指针通常是一个IClassFactory指针。

类厂的接口定义如下

// In unknwn.h
IClassFactory : public IUnknown
{
   
public:
     
virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstance( 
            
/* [unique][in] */ IUnknown *pUnkOuter,
            
/* [in] */ REFIID riid,
            
/* [iid_is][out] */ void **ppvObject) = 0;
        
     
virtual /* [local] */ HRESULT STDMETHODCALLTYPE LockServer( 
            
/* [in] */ BOOL fLock) = 0;
}
;

 

成员函数CreateInstance的后两个参数和QueryInterface的后两个含义是一致的。成员函数LockServer控制组件的生命周期。
一般情况下,客户程序或者COM库只是在创建对象的时候才使用类厂对象的接口指针,创建完后就把类厂对象丢弃(应该是销毁)。假定客户中有一个指向某个正在运行的类厂的指针,在相应的DLL被卸载之后,若客户试图使用IClassFactory指针,那末就会出现错误。因此客户需要一种手段来,防止该问题出现。IClassFactory::LockServer就是为了解决这个问题。该函数为客户提供了一种将服务器(DLL)保存在内存中,直至使用完毕的方法。客户只需调用LockServer(TRUE)来锁住相应的服务器,并在使用完毕后调用LockServer(FALSE)将其解锁。

(注:建议组件计数(记录组件服务器即DLL中目前被创建并存在的组件个数)和加锁计数使用不同的计数器,DllCanUnloadNow应同时检测这两个值)

类厂的若干特性
  首先类厂的一个实例将只能创建同某个CLSID相应的组件。这一点从CoGetClassObject将接收一个CLSID而IClassFactory::CreateInstance却不接收此参数可以看出。
  其次与某个特定CLSID相应的类厂将是由实现组件的开发人员实现的。多数情况下,类厂组件包含在与它所创建的组件相同的DLL中。

由于类厂的一个实例只同某个CLSID相应并且由于类厂和它所创建的组件是由同一开发人员实现的,因此,类厂可以知道并且确实具有它所创建的组件的一些特殊知识。

阅读更多
文章标签: dll 服务器 class
个人分类: Windows
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭