Chapter7 类厂
首先介绍组件的创建,CoCreateInstance函数。
函数声明:
STDAPI CoCreateInstance(
REFCLSID rclsid, //Class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
DWORD dwClsContext, //Context for running executable code
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives
// the interface pointer requested in riid
);
第一个是待创建的CLSID,第二个是用于聚合组件用的(表示是否聚合),第三个参数是限定所创建的组件的执行上下文,第四个参数是待使用的Interface IID。
第二个参数后面介绍,第三个参数是有四种选择:
CLSCTX_INPROC_SERVER:在同一进程中运行,必须是DLL。
CLSCTX_INPROC_HANDLER:在同一进程中,但是只实现了一部分组件。
CLSCTX_LOCAL_SERVER:进程外组件。EXE形式。
CLSCTX_REMOTE_SERVER:远程机器上运行的组件。EXE形式。
如果用户不清楚具体怎么用,还可以使用如下常量:
CLSCTX_INPROC
CLSCTX_INPROC_SERVER
CLSCTX_INPROC_HANDLER
CLSCTX_ALL
CLSCTX_INPROC_SERVER
CLSCTX_INPROC_HANDLER
CLSCTX_LOCAL_SERVER
CLSCTX_REMOTE_SERVER
CLSCTX_SERVER
CLSCTX_INPROC_SERVER
CLSCTX_LOCAL_SERVER
CLSCTX_REMOTE_SERVER
CoCreateInstance的不灵活性。
创建的过程用户不能控制。例如组件装载的内存位置,用户权限等无法实现。
由此,类厂的概念产生了。
CoCreateInstance并没有直接创建组件,而是创建的类厂。用户可以通过类厂支持的接口对类厂的创建过程进行控制。创建组件的标准接口是IClassFactory。
与CoCreateInstance类似的函数CoGetClassObject:
STDAPI CoGetClassObject(
REFCLSID rclsid, //CLSID associated with the class object
DWORD dwClsContext,
//Context for running executable code
COSERVERINFO * pServerInfo,
//Pointer to machine on which the object is to
// be instantiated
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
他们之间最大的区别就是前者指向组件,后者指向类厂。
第三个参数也不同:
COSERVERINFO: 用于对远程组件的控制。
IClassFactory
类厂有两个成员函数,CreateInstance,LockServer。
HRESULT CreateInstance(
IUnknown * pUnkOuter, //Pointer to whether object is or isn't part of
// an aggregate
REFIID riid, //Reference to the identifier of the interface
void ** ppvObject //Address of output variable that receives the
// interface pointer requested in riid
);
HRESULT LockServer(
BOOL fLock //Increments or decrements the lock count
);
CreateInstance没有接收CLSID参数,所以可见它创建的是同CoGetClassObject的CLSID一致的类厂.
IClassFactory2 增加了许可或授权的功能。
CoGetClassObject的使用较复杂,CoCreateInstance也是通过CoGetClassObject来实现的,所以大多数用CoCreateInstance
。那么使用CoGetClassObject的情况是:
1.不想用IClassFactory来创建
2.若使用同一个组件的多个实例。
类厂的特性:
1.类厂只能创建同某个CLSID相应的组件。
2.类厂组件包含在所创建的组件的dll中。
DllGetClassObject
CoGetClassObject调用此函数来实现类厂的创建。
STDAPI DllGetClassObject(
REFCLSID rclsid, //CLSID for the class object
REFIID riid, //Reference to the identifier of the interface
// that communicates with the class object
LPVOID * ppv //Address of output variable that receives the
// interface pointer requested in riid
);
DllGetClassObjec做了什么?
1.确认客户请求的类厂组件是能够创建的。
2.创建组件。
3.查询接口。
同一个Dll中的多个组件
类厂复用,一个类厂多个组件。
用一个数组或链表等的数据结构存储创建函数指针。
Dll卸载
DllCanUnloadNow
LockServer
类似于mutex的作用。
Chapter8 包容和聚合
C++中的继承概念,在COM中的表现形式,变成了接口继承。
具体有两种,包容和聚合。
1.包容:
同C++的包容类似,但是接口的包容。
图示:一个包含有一个内部组件并复用了其IY接口实现的外部组件的内部结构。
|--------------------------------------|
| |
| |---------------| |
O------|--------- | IX | |
| |---------------| |
| |
| |---------------| |
O------|----------| IY | |
| |--------|-------| |
| | |
| | |
| |-------------------------| |
| | IY | |
| |-------------------------| |
|--------------------------------------|
2.聚合
聚合是包容的一个特例,聚合不用实现内部组件的接口并明确调用请求转发给内部组件。相反,外部组件将直接把内部组件的接口返回给客户。
|--------------------------------------|
| |
| |---------------| |
O------|--------- | IX | |
| |---------------| |
| |
| |---------------| |
O------|----------| IY | |
| |---------------| |
| |
|--------------------------------------|
3.包容的实现:
实现简单,只要把查询内部组件的接口实现了,但是问题是可能会有很多函数需要实现。
4.聚合的实现:
聚合的实现比较复杂,涉及到代理和非代理的问题。
为什么会这样复杂呢,只要在调用QueryInterface的时候把内部组件接口的指针传出去就好了啊!
可是这样会出现大错误,违反了Query Interface的原则,必须是反身的,必须是对称的,必须是可传递的。
举个例子:
假设有某个聚合组件,实现了接口IX,聚合了接口IY。内部组件里实现了IY和IZ接口。当Query IZ接口时会提示不存在,
但是通过IX接口Query IY 在通过IY Query IZ就可以Query到。这就出现了接口的可传递性原则了。
为了解决这个问题,于是引出了代理和非代理,这样就是两个不同IUnknown接口。