COM原理

简介: 组件程序的装载和卸载 ------------------------------------------------------------------------------- 进程内组件的装载: 客户程序调用COM 库的 CoCreateInstance 或 CoGetClassObject ...

组件程序的装载和卸载 ------------------------------------------------------------------------------- 进程内组件的装载: 客户程序调用COM 库的 CoCreateInstance 或 CoGetClassObject 函数创建 COM 对象,在 CoGetClassObject 函数中,COM 库根据系统注册表中的信息,找到类标识符 CLSID 对应的组件程序(DLL 文件)的全路径,然后调用 LoadLibrary(实际上是 CoLoadLibrary)函数,并调用组件程序的 DllGetClassObject 引出函数。DllGetClassObject 函数创建相应的类厂对象,并返回类厂对象的 IClassFactory 接口。至此 CoGetClassObject 函数的任务完成,然后客户程序或者 CoCreateInstance 函数继续调用类厂对象的 CreateInstance 成员函数,由它负责 COM 对象的创建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID -> DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace 进程外组件的装载: 在 COM 库的 CoGetClassObject 函数中,当它发现组件程序是 EXE 文件(由注册表组件对象信息中的 LocalServer 或 LocalServer32 值指定)时,COM 库创建一个进程启动组件程序,并带上“/Embedding”命令行参数,然后等待组件程序;而组件程序在启动后,当它检查到“/Embedding”命令行参数后,就会创建类厂对象,然后调用 CoRegisterClassObject 函数把类厂对象注册到 COM 中。当 COM 库检查到组件对象的类厂之后,CoGetClassObject 函数就把类厂对象返回。由于类厂与客户程序运行在不同的进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或 COM 库得到了类厂对象,它就可以完成组件对象的创建工作。进程内对象和进程外对象的不同创建过程仅仅影响了 CoGetClassObject 函数的实现过程,对于客户程序来说是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy) 进程内组件的卸载: 只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为 0,类厂的锁计数为 0。满足这两个条件时,DllCanUnloadNow 引出函数返回 TRUE。COM 提供了一个函数 CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的 DllCanUnloadNow 函数返回 TRUE 时,就调用 FreeLibrary 函数(实际上是 CoFreeLibrary 函数)把该组件从程序从内存中卸出。该由谁来调用 CoFreeUnusedLibraries 函数呢?因为在组件程序执行过程中,它不可能把自己从内存中卸出,所以这个任务应该由客户来完成。客户程序随时都可以调用 CoFreeUnusedLibraries 函数完成卸出工作,但通常的做法是,在程序的空闲处理过程中调用 CoFreeUnusedLibraries 函数,这样做既可以避免程序中处处考虑对 CoFreeUnusedLibraries 函数的调用,又可以使不再使用的组件程序得到及时清除,提高资源的利用率,COM 规范也推荐这种做法。 进程外组件的卸载: 进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在 Windows 系统中,进程的主控函数为 WinMain。前面曾经说过,在组件程序启动运行时,它调用 CoRegisterClassObject 函数,把类厂对象注册到 COM 中,注册之后,类厂对象的引用计数始终大于 0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的载条件与 DllCanUnloadNow 中的判断类似,也需要判断 COM 对象是否还存在、以及判断是否锁计数器为 0,只有当条件满足了,进程的主函数才可以退出。从原则上讲,进程外组件程序的卸载就是这么简单,但实际上情况可能复杂一些,因为有些组件程序在运行过程中可以创建自己的对象,或者包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为 0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可以增加一个“用户控制”标记 flag,如果 flag 为 FALSE,则可以按简单的方法直接退出程序即可;如果 flag 为 TRUE,则表明用户参与了控制,组件进程不能马上退出,但应该调用 CoRevokeClassObject 函数以便与 CoRegisterClassObject 调用相响呼应,把进程留给用户继续进行。如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可以采取两种办法:第一种方法,把应用隐藏起来,并把 flag 标记设置为 FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是,调用 CoDisconnectObject 函数,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时可以也使用,以保证系统完成一些高优先级的操作。 ------------------------------------------------------------------------------- COM 库常用函数 ------------------------------------------------------------------------------- 初始化函数 CoBuildVersion 获得 COM 库的版本号 CoInitialize COM 库初始化 CoUninitialize COM 库功能服务终止 CoFreeUnusedLibraries 释放进程中所有不再使用的组件程序 GUID 相关函数 IsEqualGUID 判断两个 GUID 是否相等 IsEqualIID 判断两个 IID 是否相等 IsEqualCLSID 判断两个 CLSID 是否相等 (*为什么要3个函数) CLSIDFromProgID 字符串组件标识转换为 CLSID 形式 StringFromCLSID CLSID 形式标识转化为字符串形式 IIDFromString 字符串转换为 IID 形式 StringFromIID IID 形式转换为字符串 StringFromGUID2 GUID 形式转换为字符串(*为什么有 2) 对象创建函数 CoGetClassObject 获取类厂对象 CoCreateInstance 创建 COM 对象 CoCreateInstanceEx 创建 COM 对象,可指定多个接口或远程对象 CoRegisterClassObject 登记一个对象,使其它应用程序可以连接到它 CoRevokeClassObject 取消对象的登记 CoDisconnectObject 断开其它应用与对象的连接内存管理函数 CoTaskMemAlloc 内存分配函数 CoTaskMemRealloc 内存重新分配函数 CoTaskMemFree 内存释放函数 CoGetMalloc 获取 COM 库内存管理器接口 ------------------------------------------------------------------------------- HRESULT 类型 ------------------------------------------------------------------------------- 大多数 COM 函数以及一些接口成员函数的返回值类型均为 HRESULT 类型。HRESULT 类型的返回值反映了函数中的一些情况,其类型定义规范如下: 31 30 29 28 16 15 0 |-----|--|------------------------|-----------------------------------| 类别码 (30-31) 反映函数调用结果: 00 调用成功 01 包含一些信息 10 警告 11 错误自定义标记(29) 反映结果是否为自定义标识,1 为是,0 则不是;操作码 (16-28) 标识结果操作来源,在 Windows 平台上,其定义如下: #define FACILITY_WINDOWS 8 #define FACILITY_STORAGE 3 #define FACILITY_RPC 1 #define FACILITY_SSPI 9 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DISPATCH 2 #define FACILITY_CERT 11 操作结果码(0-15) 反映操作的状态,WinError.h 定义了 Win32 函数所有可能返回结果。 以下是一些经常用到的返回值和宏定义: S_OK 函数执行成功,其值为 0 (注意,其值与 TRUE 相反) S_FALSE 函数执行成功,其值为 1 S_FAIL 函数执行失败,失败原因不确定 E_OUTOFMEMORY 函数执行失败,失败原因为内存分配不成功 E_NOTIMPL 函数执行失败,成员函数没有被实现 E_NOTINTERFACE 函数执行失败,组件没有实现指定的接口 不能简单地把返回值与 S_OK 和 S_FALSE 比较,而要用 SECCEEDED 和 FAILED 宏进行判断。 =============================================================================== ⊙ 第四章 COM 特性 =============================================================================== 可重用性:包容和聚合 ------------------------------------------------------------------------------- 包容模型: 组件对象在接口的实现代码中执行自身创建的另一个组件对象的接口函数(客户/服务器模型)。这个对象同时实现了两个(或更多)接口的代码。 聚合模型: 组件对象在接口的查询代码中把接口传递给自已创建的另一个对象的接口查询函数,而不实现该接口的代码。另一个对象必须实现聚合模型(也就是说,它知道自己正在被另一个组件对象聚合),以便 QueryInterface 函数能够正常运作。 在组件对象被聚合的情况下,当客户请求它所不支持的接口或者请求 IUnknown 接口时,它必须把控制交给外部对象,由外部对象决定客户程序的请求结果。聚合模型体现了组件软件真正意义上的重用。 聚合模型实现的关键在 CoCreateInstance 函数和 IClassFactory 接口: HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); // class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0; 其中 pUnknownOuter 参数用于指定组件对象是否被聚合。如果 pUnknownOuter 参数为 NULL,说明组件对象正常使用,否则说明被聚合使用,pUnknownOuter 是外部组件对象的接口指针。 聚合模型下的被聚合对象的引用计数成员函数也要进行特别处理。在未被聚合的情况下,可以使用一般的引用计数方法。在被聚合时,由客户调用 AddRef/Release 函数时,必须转向外部组件对象的 AddRef/Release 方法。这时,外部组件对象要控制被聚合的对象必须采用其它的引用计数接口。 ------------------------------------------------------------------------------- 进程透明性 (待学) 安全性(待学) 多线程特性(待学) ------------------------------------------------------------------------------- =============================================================================== ⊙ 第五章 用 Visual C++ 开发 COM 应用 =============================================================================== Win32 SDK 提供的一些头文件的说明 ------------------------------------------------------------------------------- Unknwn.h 标准接口 IUnknown 和 IClassFacatory 的 IID 及接口成员函数的定义 Wtypes.h 包含 COM 使用的数据结构的说明 Objidl.h 所有标准接口的定义,即可用于 C 语言风格的定义,也可用于 C++ 语言 Comdef.h 所有标准接口以及 COM 和 OLE 内部对象的 CLSID ObjBase.h 所有的 COM API 函数的说明 Ole2.h 所有经过封装的 OLE 辅助函数 ------------------------------------------------------------------------------- 与 COM 接口有关的一些宏 ------------------------------------------------------------------------------- DECLARE_INTERFACE(iface) 声明接口 iface,它不从其他的接口派生 DECLARE_INTERFACE_(iface, baseiface) 声明接口 iface,它从接口 baseiface 派生 STDMETHOD(method) 声明接口成员函数 method,函数返回类型为 HRESULT STDMETHOD_(type, method) 声明接口成员函数 method,函数返回类型为 type 文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/c++/cppxl/20081118/151939_3.html

相关文章
|
存储 Kubernetes 负载均衡
k8s是什么以及它的原理和如何去使用它?
k8s是什么以及它的原理和如何去使用它?
|
6月前
|
机器学习/深度学习 数据采集 人工智能
奥卡姆剃刀原理
奥卡姆剃刀原理“【5月更文挑战第17天】”
63 4
|
3月前
|
数据采集 自然语言处理 算法
Sekiro原理
这篇文章介绍了SEKIRO,一个多语言的、分布式的、与网络拓扑无关的服务发布平台,它支持通过编写不同语言的handler将功能发布到中心API市场,并通过RPC方式调用,特别适用于受限环境下的功能外放和服务提供。
57 0
|
11月前
|
Kubernetes 监控 调度
k8s 自身原理 4
k8s 自身原理 4
|
11月前
|
Kubernetes 监控 Linux
k8s 自身原理 5
k8s 自身原理 5
|
11月前
|
存储 Kubernetes API
k8s 自身原理 1
k8s 自身原理 1
|
11月前
|
Kubernetes Cloud Native 调度
k8s 自身原理 2
k8s 自身原理 2
|
存储 缓存 算法
四、深入剖析【离屏渲染】原理
深入剖析【离屏渲染】原理
512 0
四、深入剖析【离屏渲染】原理
|
监控 Dubbo 搜索推荐
ShutdownHook原理
有了ShutdownHook我们可以 在进程结束时做一些善后工作,例如释放占用的资源,保存程序状态等 为优雅(平滑)发布提供手段,在程序关闭前摘除流量
299 0
ShutdownHook原理
|
存储 Unix 程序员
说了这么多次 I/O,可你知道其中的原理么(一)
现在让我们转向对 I/O 软件的研究,I/O 软件设计一个很重要的目标就是设备独立性(device independence)。啥意思呢?这意味着我们能够编写访问任何设备的应用程序,而不用事先指定特定的设备。
说了这么多次 I/O,可你知道其中的原理么(一)