第7章 类厂
相信有了前六章的知识积累,学些以后的章节将会很顺利。本章实现了一个真正的COM组件,并通过client客户端来使用这个组件。学完这章,对COM组件的最基本的东西就已经学完了,可以实现一个真正意义上的组件,后续几章就是在此基础上进行的升华,例如组件的复用,EXE中的组件,多线程等等,也同样重要。
本章将介绍类厂,类厂是能够创建其他组件的组件,并且可以使用同一个类厂来创建不同的CLSID所对应的组件,也就是说,在同一个DLL中,可以包含多个组件(组件是用C++类来实现的),并可以使用同一个类厂来创建它们,我想这就是类厂名字的由来。
CoCreateInstance函数是创建组件并获得IUnknown接口,虽然在使用过程中并没有使用类厂,但是它也是按照一定方法通过类厂来创建组件的。
CoCreateInstance
CoCreateInstance函数是COM库的函数,函数原型如下
HRESULT __stdcall CoCreateInstance(const CLSID &clsid,
IUnknown *pIUnkownOuter, //Outer Component
DWORD dwClsContext, //Server Context
const IID &iid,
void **ppv);
其中前四个是输入参数,最后一个是输出参数。第一个参数clsid是所创建组件的CLSID,第二个参数pIUnkownOuter是聚合组件需要用的,将在第8章介绍,第三个参数dwClsContext是限定所创建组件的执行上下文,第四个参数iid是所创建组件的接口的IID,第五个参数ppv将返回该接口的指针。
dwClsContext
dwClsContext可以控制所创建是与客户在相同的进程中运行,还是在不同的进程中运行,或者是在另外一台机器上运行。此参数可以是如下值的组合
CLSCTX_INPROC_SERVER 客户希望创建在同一进程中运行的组件,因此组件必须是在DLL中实现。
CLSCTX_INPROC_HANDLER 客户希望创建进程中处理器。
CLSCTX_LOCAL_SERVER 客户希望创建一个在同一机器上的另外一个进程中运行的组件。组件是由EXE实现的。
CLSCTX_REMOTE_SERVER 客户希望创建一个在远程机器上运行的组件。分布式COM组件。
在OBJBASE.H中定义了一些上述值的组合。
CoCreateInstance的具有一定的不灵活性,解决问题的办法是使用专门用于创建所需组件的组件,这个组件就是类厂。
类厂
某个特定的类厂可以创建某个特定CLSID相对应的组件,客户可以通过类厂提供的接口来对组件的创建过程进行控制。客户使用CoCreateInstance所创建的组件实际上是通过类厂的IClassFactory创建的,使用类厂创建组件的步骤是首先创建类厂,然后使用IClassFactory创建所需的组件。
1.创建类厂
COM库函数CoGetClassObject创建同某个CLSID相应的类厂。函数原型如下
HRESULT __stdcall CoGetClassObject(const CLSID &clsid,
DWORD dwClsContext,
COSERVERINFO *pServerInfo,
const IID &iid,
void **ppv);
同CoCreateInstance非常相似。第一个参数const CLSID&待创建组件的CLSID,第二个参数DWORD dwClsContext是待创建组件的执行上下文,第三个参数COSERVERINFO*用于远程组件的访问,将在第10章讨论,第四个参数const IID&是IClassFactory接口的IID,第五个参数返回IClassFactory接口的指针。
2.类厂组件
IClassFactory
大多数组件是使用IClassFactory来创建的,原型如下
interface IClassFactory:IUnknown
{
HRESULT __stdcall CreateInstance(IUnkown *pUnknownOuter, const IID &id, void **ppv);
HRESULT __stdcall LockServer(BOOL bLock);
};
IClassFactory::CreateInstance函数的第一个参数IUnknown*是组件聚合使用的,将在第8章介绍,后两个参数跟CoCreateInstance后两个参数作用相同,将在创建组件的
同时返回此组件的某个接口指针。可以看到IClassFactory::CreateInstance并没有接收一个CLSID参数,这意味着此函数将只能创建同某个CLSID——即传给CoGetClassObject的参数CLSID相应的组件。
IClassFactory::CreateInstance和DllGetClassObject的实现是相同的,这两个函数都将创建一个组件然后向它查询某个接口。
在两种情况下使用创建类厂再创建组件的方法,而不是直接使用CoCreateInstance的方法直接创建组件
(1)想使用IClassFactory2来创建组件。IClassFactory2是Microsoft定义的另外一个接口,此接口在IClassFactory的基础上增加了获取组件接口的许可权限功能。
(2)需要创建一个组件的多个实例。这样只需创建相应的类厂一次,而CoCreateInstance需要为每一个组件实例分别创建并释放相应的类厂。
类厂的特性
(1)类厂将只能给你创建同某个CLSID相应的组件。
(2)与某个特定CLSID相应的类厂是由组件开发人员来实现的。大多数情况下,类厂组件包含在它所创建的组件的同一个的DLL中。
类厂的创建
客户通过CoGetClassObject来创建类厂,这就需要在组件的DLL中实现一个特定的函数,此函数名为DllGetClassObject,由COM库函数CoGetClassObject调用,函数原型如下
STDAPI DllGetClassObject(const CLSID &clsid, const IID &iid, void **ppv);
函数的三个参数同CoGetClassObject中传入的参数的意义相同。
通过类厂来创建组件的示意图如下,COM库函数CoGetClassObject将根据传入参数CLSID查询注册表,装载组件所在的DLL库。
首先客户调用CoGetClassObject,COM库实现CoGetClassObject函数,组件所在的DLL输出DllGetClassObject函数由CoGetClassObject函数调用,它返回IClassFactory指针,客户调用IClassFactory来创建相应