COM组件的安装与卸载
(1)进程内组件的安装
客户调用COM库的CoCreateInstance或CoGetClassObject创建COM对象时,在CoGetClassObject中,COM库根据注册表中的信息,找到类标识符CLSID对应的组件程序(DLL)的全路径并调用CoLoadLibrary,再调用组件程序的DllGetClassObject导出函数。
如:ASIO asiosample.dll注册及注册表信息变化
DllGetClassObject创建相应的类厂对象,并返回类厂对象的IClassFactory接口。至此CoGetClassObject任务完成,然后客户程序或CoCreateInstance继续调用类厂对象的CreateInstance成员函数,由它负责COM对象的创建工作。
如:ASIO 驱动加载loadAsioDriver的AsioDrivers::loadDriver功能。
(2)进程外组件的安装
在COM库的CoGetClassObject中,当发现组件程序是EXE(由注册表组件对象信息中的LocalServer或LocalServer32值指定)时,COM库创建一个进程启动组件程序,并带上/Embedding命令行参数,然后等待组件程序;组件程序启动后,当它检查到/Embedding命令行参数时,就会创建类厂对象,然后调用CoRegisterClassObject把类厂对象注册到COM中。
当COM库检查到组件对象的类厂之后,CoGetClassObject就返回类厂对象。由于类厂与客户运行在不同进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或COM库得到了类厂对象,就可完成组件对象的创建工作。进程内对象和进程外对象的不同创建过程仅仅影响了CoGetClassObject的实现过程,对客户程序来说是完全透明的。
(3)进程内组件的卸载
只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为0,类厂的锁计数为0。满足这两个条件时,DllCanUnloadNow导出函数返回TRUE。
COM库提供了CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的DllCanUnloadNow返回TRUE时,就调用FreeLibrary(实际是CoFreeLibrary)把该组件从内存中卸出。由谁来调用CoFreeUnusedLibraries呢?因为组件执行过程中它不可能把自己从内存中卸出,所以这个任务应该由客户完成。客户程序随时都可调用CoFreeUnusedLibraries完成卸载工作,通常做法是,在程序空闲处理过程中调用CoFreeUnusedLibraries,这样做既可避免程序中处处考虑对CoFreeUnusedLibraries的调用,又可使不再使用的组件程序得到及时清除,提高了资源的利用率,COM规范也推荐这种做法。
(4)进程外组件的卸载
进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在Windows系统中,进程的主控函数为WinMain。
前面曾说过,在组件程序启动运行时,它调用CoRegisterClassObject函数,把类厂对象注册到COM中,注册之后,类厂对象的引用计数始终大于0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的卸载条件与DllCanUnloadNow中的判断类似,也需要判断COM对象是否存在、以及判断是否锁计数器为0,只有当条件满足了,进程的主函数才可退出。
从原则上讲,进程外组件程序的卸载就是这么简单,但实际情况可能复杂一些,因为有些组件程序在运行过程中可创建自己的对象,或包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。
例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可增加一个”用户控制”标记flag,如果flag=FALSE,则可按简单方法直接退出程序即可;如果flag=TRUE,则表明用户参与了控制,组件进程不能马上退出,应该调用CoRevokeClassObject以便与CoRegisterClassObject调用相呼应,把进程留给用户继续进行。
如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可采取两种办法:第一种方法,把应用隐藏起来,并设置flag=FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是调用CoDisconnectObject,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时也可使用,以保证系统完成一些高优先级的操作。