类厂意指,制造出组件的组件。
COM函数CoCreateInstance是用来创建组件的最基本和最常用的方法。
但它其实也是通过类厂来创建的。只不过过程统一化了。
换句话说,CoCreateInstance只是类厂创建的一个具体应用。
要创建更灵活的组件,一定会使用类厂。
CoCreateInstance的限制
HRESULT __stdcall CoCreateInstance(
const CLSID& clsid, // 待创建组件的clsid
IUnknown* pIUnknownOuter, // 聚合组件用
DWORD dsClsContext, // 组件的上下文
const IID & iid, // 组件上待使用的接口
void ** ppv // 返回接口的指针
)
dsClscontext可能值包括:
CLSCTX_INPROC_SERVER 客户希望创建在同一进程中的组件。必须是DLL。
CLSCTX_INPROC_HANDLER 客户希望创建进程中处理器。(只实现了某组件的进程中的部分)
CLSCTX_LOCAL_SERVER 客户希望创建一个在同一机器上另外一个进程中运行的。exe实现。
CLSCTX_REMOTE_SERVER 希望创建一个在远程机器上运行的组件。需分布式COM正常工作。
使用CoCreateInstance创建组件完成后,想控制组件加载到内存中何处或者检查客户是否有创建权限都是做不到的。
类厂
CoCreateInstance实际上也是创建了一个被称作类厂的组件。所需的组件也是由该类厂来创建的。
类厂组件存在的意义就是一件事,创建其他组件。某个特定类厂创建特定CLSID的组件。
客户可以通过类厂的接口来控制组件创建的过程。创建组件的标准接口是IClassFactory。
COM库中的CoGetClassObject
COM库中CoGetClassObject也接收一个CLSID作为参数,但返回的是类厂中的某个接口的指针。
HRESULT __stdcall CoGetClassObject(
const CLSID& clsid, // 待创建组件的clsid
DWORD dsClsContext, // 组件的上下文
COSERVERINFO * pServerInfo // Reserved for DCOM
const IID & iid, // 组件上待使用的接口
void ** ppv // 返回类厂中某个接口的指针
)
参数与CoCreateInstance相比,少了聚合组件用的参数,多了一个远程组件的参数。
这里的返回值oov是指类厂中的某个接口指针,而CoCreateInstance是组件的某个接口。
IClassFactory
类厂接口具有两个成员函数:CreateInstance,和LockServer。
HERESULT __stdcall CreateInstance(
IUnknown * pUnknownOuter,
const IID& iid,
void ** ppv);
CreateInstance并不接收CLSID参数,意味着一个类厂只能请求一个CLSID的接口指针。而CLSID是在创建类厂时赋予的,也就是说同样的类厂只能创建同样的组件。
HERESULT __stdcall LockServer(BOOL bLock);
因为CoCreateInstance实际上也是首先创建相应的类厂,然后再创建组件,因此要创建同一组件的多个实例的话,使用CoGetClassObject首先创建类厂,然后类厂再多次CreateInstance创建实例,这样要高效得多。
DLL的卸载
CoLoadLibrary装载DLL,卸载DLL使用 CoFreeUnusedLibraries。
(COM函数CoLoadLibrary调用的是LoadLibrary)
在CoFreeUnusedLibrary内部,使用DllCanUnloadNow询问Dll是否可以被卸载。
但这里卸载掉DLL之后,如果类厂中还在使用DLL中某个接口的话……IClassFactory::LockServer的作用就在此。客户首先调用LockServer(TRUE)锁定,并在使用完毕后LockServer(False)解锁。