一、CoCreateInstance
他是创建组件最简单的方法。 但他的灵活性不足以使之满足所有组件的需求。
HRESULT _stdcall CoCreateInstance( const CLSID& clsid, IUnknown * pIUnknownOuter, DWORD dwClsContext, const IID& iid, void ** ppv);
2.用于聚合组件的
3.限定所创建的组件的执行上下文
4.iid为组件上待使用的接口的IID
通过将一个IID传给CoCreateInstance 客户将无需在创建组件之后去调用其QueryInterface函数。
下面是本函数的使用:
IX* pIX = NULL; HRESULT hr = ::CoCreateInstance(CLSID_Compnent1, NULL, CLSCTX_INPROC_SERVER, IID_IX, (void**)&pIX); if(SUCCEEDED(hr)) { pIX->Fx(); pIX->Release(); }
其缺乏灵活性:
CoCreateInstance没有给客户提供一种能够控制组件创建过程的方法,当其完成之后,组件实际上已经建立好了。
建立一个组建后,想要控制将组件装载到内存中何处或检查客户是否有权限来创建该组件基本上已经不可能了。
我们要如何控制组建的创建过程,这是关键问题。 我们不需要关心组件的初始化,在创建好组建后,可以通过一个接口来初始化它。但是建立好一个组件之前是无法获取它的某个接口指针的。 因此也就无法对组件的创建加上限制条件了。
解决方法就是使用另外一个专门用于创建所需组件的组件。就是类厂。
二、类厂
CoCreateInstance实际上没有直接创建COM组件,而是创建了一个被称作类厂的组件。 你所需要的组件是由类厂创建。
类厂组件的唯一功能就是创建其他的组件。 (某个特定类厂将创建只同某个特定的CLSID相应的组件。)
创建组件的标准接口是IClassFactory。 CoCreateInstance实际上是通过其创建的。
类厂创建组件:
1.、创建类厂本身
CoGetClassObject 创建相应的类厂
HRESULT _stdcall CoGetClassObject( const CLSID& clsid, DWORD dwClsContext, COSERVERINFO * pServerInfo, const IID& iid, void ** ppv);
CoCreateInstance返回指向组件中某个接口的指针。
客户可以用CoGetClassObject所返回的指针来创建所需的组件。
2、使用一个接口如:IClassFactory来创建所需的组件。
interface IClassFactory:IUnknown { HRESULT _stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void ** ppv); HRESULT _stdcall LockServer(BOOL bLock); }
还有个方法是 IClassFactory2。 他在IClassFactory基础上增加了许可或权限功能。
HRESULT CoCreateInstance(const CLSID& clsid, IUnknown* pUnknownDuter, DWORD dwClsContext, const IID& iid, void** ppv) { *ppv = NULL; IClassFactory * pIFactory = NULL; HRESULT hr = CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void**)&pIFactory); if(SUCCEEDED(hr)) { hr = pIFactory->CreateInstance(pUnknownOuter, iid, ppv); pIFactory->Release(); } return hr; }
1、如果用不同于IClassFactory的某个创建接口来创建组件,必须使用CoGetClassObject。
2、若需要创建同一组件的多个实例,那么使用CoGetClassObject将可以获得更高的效率。
类厂只不过是创建其他组件的一个简单组件。
类厂的一个实例只能创建同某个CLSID相应的组件。
与某个特定CLSID相应的类厂将是由实现组件的开发人员实现的。 通常,类厂组件包含在与它所创建的组件相同的dll中。
三、类厂的实现
CoGetClassObject需要dll中一个特定的函数来创建组件的类厂:
STDAPI DllGetClassObject( const CLSID& clsid, const IID& iid, void** ppv);
DLLGetClassObject的任务就是创建客户所请求的类厂。
看图:
其中实线是相应元素已经被建立好了并且是活动的,虚线表示相应的元素不再是活动的。线上的灰色条纹分别表示每一个操作的生命期。水平线表示将流程控制从一个结构化元素传递给下一个元素的函数调用。
对图的解释:
最后介绍一下dll的入口:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { if(dwReason == DLL_PROCESS_ATTACH) { g_hModule = hModule; } return TRUE; }
四、同一DLL中的多个组件
DLL不等价于组件,相当于一个组件服务器。 将dll当成是在收到客户的请求后提供相应的组件的想法是很好的。 dll实际上是为组件实现的分发提供了一种方法。
当你在设计类厂和组件的时候,如果设计的好的话,可以做到只用一耳光类厂的实现来完成所有组件的创建。
一般我们为每一个组件编写一个简单的函数,这个函数将使用new操作符来创建此组件并返回一个IUnknown指针。 然后可以用指向这些函数指针的指针构成一个表格,而对此表格的访问将用各组件的clsid作为索引。
dllgetclassobject将查找该表格以获取相应的创建函数指针,并在创建好相应的类厂之后将此指针传给此类厂。
之后类厂通过此指针来调用相应的组件创建函数,而不再直接通过new来完成
如图:
五、DLL的卸载
DLL装载到内存,由LoadLibrary函数完成。
装载后我们需要释放,com库实现了一个名为CoFreeUnusedLibraries函数,释放那些不再需要的库所占用的内存空间。
在程序的空闲期间,客户应周期性地调用此函数。
CoFreeUnusedLibrary调用DllCanUnloadNow询问DLL是否可以被卸载掉。
DllCanUnloadNow将高速COM 它是否仍在提供对任意对象的支持。 若DLL不再提供任何组件了,那些CoFreeUnusedLibraries就可以将此DLL卸载掉。
DLL为决定它是否仍在提供对组件的支持,它将维护一个关于组件的计数值。
static long g_cComponents = 0;
IClassFactory::CreateInstance或者构造函数可以将g_cComponents值增大。析构函数将其值建校。
g_cComponents为零时,那些DllCanUnloadNow将给出肯定的回复。
上述方式是统计DLL提供的组件。
而是用统计类厂更为合理。
一个正在运行的类厂并不足以保证将其某个服务器装载在内存中。
进程外服务器的启动关闭和进程内服务器的启动关闭是不同的。
我们将某个拥有正在运行的类厂的DLL 从内存中卸载将会给客户带来麻烦,如果可以中有一个指向某个正在运行的类厂的指针,在相应的dll被卸载之后,若客户视图使用IClassFactory指针,那么显然就会出错。
我们需要 防止当它想在某个函数的作用域范围之外使用一个IClassFactory指针时DLL被从内存中卸载掉。
IClassFactory::LockServer的作用就是防止~~
它提供一种将服务器保存在内存中,直到使用完毕。LockServer(TRUE)锁住服务器。 LockServer(FALSE)解锁。
2013.8.10
jofranks 于南昌