《COM原理与应用》学习笔记——COM的实现

一、进程内组件和进程外组件

前面说过,COM组件支持进程内组件和进程外组件两种方式。进程内组件的优势就是快、方便,但是如果组件里面有哪个地方崩溃了也会导致主进程的崩溃。进程外组件虽然比较慢,但是很稳定。如果组件某个地方崩溃了,最多就是没有结果,不会导致主进程的崩溃(比如,看到了浏览器插件崩溃了,没见到浏览器崩溃吧)。用户在使用COM组件的时候,实际上是感觉不出来进程内组件和进程外组件的区别的。用户在创建COM对象的时候都可以使用一样的方式,这个在本文的后面会详细介绍。客户程序与进程内组件和进程外组件的通信方式实际上差别是相当大的,但是COM库的实现保证了客户程序创建COM对象的进程透明特性。下面来说说进程内组件和进程外组件的使用方式:

1.进程内组件的方式:

客户程序和组件程序是处于同一个进程中的,由于在同一进程中,所以客户程序和组件程序是共享资源的,客户程序可以直接调用接口指针的成员函数。进程内组件程序也被成为进程内服务程序,由于进程内组件的执行效率非常高,所以进程内组件也有非常广泛的应用。进程内组件主要是以动态链接库(DLL)的形式存在(其实有没有其他方式我也不是很清楚~)。在客户程序运行时,客户程序将它所需的DLL的文件映像映射到调用进程的地址空间中(详见《Windows核心编程(第五版)》动态链接库的部分)。所以,DLL也是独立存在的,客户程序A可以使用这个DLL,客户程序B也可以使用这个DLL。但是在调用DLL中的函数之前,客户程序需要知道一些关于这个DLL的一些信息,这样客户程序才能正常调用DLL中的函数。这个信息也就是函数导出信息。这些导出信息的生成有很多方式,可以使用define XXX extern "C" __declspec(dllexport)的方式来生成(这种方式详见《Windows核心编程(第五版)》),但是COM组件一般是不会使用这种方式的。还有一种是写def文件,明确指出需要导出的函数(这种方式可以参见MSDN以及网上的一些介绍,《Windows核心编程(第四版)》中也有介绍,这种方式也很简单)。总的来说,使用进程内组件主要就是使用DLL装载的方式来实现对组件的使用。

2.进程外组件的方式:

这种方式中,客户程序和组件程序不在一个进程中。组件程序会单独跑一个进程,这样就不能和客户程序共享资源了。在Windows平台中,一般使用EXE程序来实现进程外组件。由于客户程序和组件程序处在不同的进程中,肯定会出现跨进程调用函数和传递数据的问题。这时就需要使用进程通信的方式了,关于进程通信,其实我也不懂,但是Windows中的进程通信方式很多。这里我也只了解了一下COM组件的进程间通信。COM采用了本地过程调用(LPC)和远过程调用(RPC)的方式进行进程间的通信。其中LPC用于本地机器的进程间通信,而RPC则用于远程计算机的进程间通信。一个程序如果需要调用其他进程的系统服务,虽然感觉上调用的是一个系统函数而已,这其中实际上也是使用了进程间通信。系统进程一直都在进行,所以要调用系统服务需要使用进程间通信。客户程序调用的实际上是系统的DLL模块,这个DLL一般叫作存根DLL。然后这个存根DLL再和系统进程进行通信,调用系统的服务。COM的进程外组件也是通过类似的方式来进行调用的。只不过调用的是一个代理DLL,而COM组件程序则使用的是存根DLL。然后代理DLL和存根DLL来实现进程间的通信。过程如下图所示:

二、COM对象的管理

COM组件要供给其他客户程序的使用。要使用这些COM组件就得有这些COM组件的信息才能调用COM库的函数来使用这些COM组件。在Windows操作系统中,COM组件的这些信息都保存在注册表中的。这些信息主要有GUID和文件的保存目录。一般来说,组件的创建工作都是由COM库来完成的,但是COM库的很多工作都是依赖于注册表的。在注册表的根节点下有HKEY_CLASSES_ROOT、HKEY_CURRNET_USER、HKEY_LOCAL_MACHINE、HKEY_USERS、HKEY_CURRENT_CONFIG、HKEY_DYN_DATA这6个子键。COM信息一般都保存在HKEY_CLASSES_ROOT的CLSID子键下的。大家可以去看一看,CLSID子键下有很多的数字键,那其实就是传说中的GUID。如果组件是进程外组件,这些子键下会保存代理DLL和存根DLL的信息,这个子键叫做ProxyStubClsid或者ProxyStubClsid32。如果组件是进程内组件的话,有一个子键会叫做InprocServer32。组件对象除了可以用CLSID来标识以外,也可以使用字符串对组件进行命名,这样的名字信息叫做ProgID,这个信息也是以子键保存下来的。如果想要让自己的组件能够被其他程序使用,一定得在注册表中添加组件的信息。这里就不多讲这个东西了,什么时候专门写一篇来写COM组件的注册表信息和其修改。

三、谁来创建COM对象——类厂

讲了这么多也没有说COM对象到底实际上是如何建立的。客户程序通过调用COM库函数来创建COM对象,COM库则通过调用类厂的接口函数来创建COM对象。类厂的代码定义如下:

1 IClassFactory : public IUnknown
2 {
3     public:
4         virtual HRESULT STDMETHODCALLTYPE CreateInstance( IUnknown *pUnkOuter,  const IID &iid,  void **ppv) = 0;
5
6         virtual HRESULT STDMETHODCALLTYPE LockServer( BOOL fLock) = 0;
7 };

类厂本身实际上也是一个COM对象,所以类厂也会继承自IUnknown。类厂中有个接口函数CreateInstance,这个CreateInstance就是COM库用来创建COM对象的函数。每个类厂只针对特定的COM对象,所以CreateInstance非常清楚自己需要创建什么COM对象。其中pUnknownOuter用于对象类被聚合的情况,一般情况下填NULL就可以了。第二个参数iid则为对象创建完成后客户端程序应该得到的初始接口IID。最后一个参数ppv则保存的是接口指针。另一个接口函数LockServer则用于控制COM组件的生命周期。如果一个客户程序希望在创建了COM对象以后继续使用类厂在以后创建COM对象,那么就应该调用LockServer函数来将组件锁顶起来,不让释放。这样主要是防止用户保存了类厂的接口指针,而接口指针在创建完成以后就被释放,以后如果再用这个接口指针创COM对象则会发生内存访问的错误。所以需要将组件锁起来,当不再继续使用的时候再释放掉。

四、谁来建立类厂

COM对象是由类厂建立的,但是类厂又是谁来建立的了。非常明显,类厂不可能是由类厂的类厂来建立的,类厂是由COM库直接来建立的。所有类厂的接口函数原型都是一模一样的,所以COM库可以调用所有的类厂接口函数。所以COM库可以直接调用类厂的接口函数。这样,客户端程序就可以通过调用COM库提供的函数来间接调用类厂的接口函数来创建COM对象了。

关于COM对象的具体的创建则留在后面的博客再来讲啦。

时间: 2024-10-02 11:42:37

《COM原理与应用》学习笔记——COM的实现的相关文章

黑马公开课——运行原理与GC学习笔记

.NET Framework 程序的运行原理 .NET Framework的组成:(1)基础类库(BCL):使用线程的类来完成编程,对于不存在的类,就自己编写:(2)编译工具:将源文件,编译成"程序集"(exe或dll等)[.NET环境中,MSIL=CIL=IL](3)公共语言运行时(CLR):执行前检测.编译:执行到了某个方法时才编译这个方法的代码[即时编译器(JIT)]编译过程:.NET源代码(C#)-->通过C#编译器编译成程序集[程序集中包括:元数据(一个表,显示了程序中

Web常见安全漏洞原理及防范-学习笔记

公司在i春秋上面报的一个课程.http://www.ichunqiu.com/course/55885,视频学习. OWASP攻击类型排名 www.wooyun.com 中报的漏洞,大多是注入.

数据库原理与技术学习笔记

数据库经历的三个阶段: 1.人工管理阶段: 数据的管理者:人 数据面向的对象:某一应用程序 数据的共享程度:无共享,冗余度极大 数据的独立性:不独立,完全依赖于程序 数据的结构化:无结构 数据控制能力:应用程序自己控制 2.文件系统阶段:特点 数据的管理者:文件系统 数据面向的对象:某一应用程序 数据的共享程度:共享性差,冗余度大 数据的独立性:独立性差 数据的结构化:记录内有结构,整体无结构 数据控制能力:应用程序自己控制 3.数据库系统阶段:特点 数据的管理者:数据库管理系统 数据面向的对象

不错的Spring学习笔记(转)

Spring学习笔记(1)----简单的实例 ---------------------------------   首先需要准备Spring包,可从官方网站上下载.   下载解压后,必须的两个包是spring.jar和commons-logging.jar.此外为了便于测试加入了JUnit包.   在Myeclipse中创建Java项目.   编写一个接口类,为了简单,只加入了一个方法.   Java代码   1.package com.szy.spring.interfacebean;  

马哥学习笔记三十二——计算机及操作系统原理

缓存方式: 直接映射 N路关联 缓存策略: write through:通写 write back:回写 进程类别: 交互式进程(IO密集型) 批处理进程(CPU密集型) 实时进程(Real-time) CPU: 时间片长,优先级低IO:时间片短,优先级高 Linux优先级:priority 实时优先级: 1-99,数字越小,优先级越低 静态优先级:100-139,数据越小,优先级越高 实时优先级比静态优先级高 nice值:调整静态优先级   -20,19:100,139   0:120 ps

编译原理学习笔记 -- 绪论1

1. 语言处理器 语言处理系统 _________ 经过预 _______ 源程序 --> |预处理器| --> 处理的 --> |编译器| --> 目标汇编程序 -------- 源程序 ------- _______ 可重定位的 ______________ --> |汇编器| --> 机器代码 --> |链接器/加载器| --> 目标机器代码 ------- -------------- ↑ 库文件/可重定位对象文件 预处理器:把源程序聚合在一起,并宏

最大熵学习笔记(二)最大熵原理

  生活中我们经常听到人们说"不要把鸡蛋放到一个篮子里",这样可以降低风险.深究一下,这是为什么呢?其实,这里边包含了所谓的最大熵原理(The Maximum Entropy Principle).本文为一则读书笔记,将对最大熵原理以及由此导出的最大熵模型进行介绍,重点给出其中所涉及数学公式的理解和详细推导. 相关链接 最大熵学习笔记(零)目录和引言 最大熵学习笔记(一)预备知识 最大熵学习笔记(二)最大熵原理 最大熵学习笔记(三)最大熵模型 最大熵学习笔记(四)模型求解 最大熵学习笔

php学习笔记day1--基本概念,及原理

作为一个php新手,我一直在寻找着一种快速而有效的学习方法,最后发现,快速掌握知识最快的方法就是输出,把自己学过的知识从新整理,拿出来给大家看,同时也要学会总结归纳,同一些高手交流,才能最快把知识融入自己,从而强化技能,提升能力. 博客园是一个学习的好地方,相同的问题,有不同的解决方案,能够很快的扩大自己的见识和知识体系.能跟更多爱好相同的人在一起讨论问题,是一件很令人开心的事情. 第一天学习笔记. 一.概念了解 1.什么是ip地址 互联网上的每一台电脑或者设备,都有唯一的编号,这个编号不会重复

[原创]java WEB学习笔记70:Struts2 学习之路-- 输入验证,声明式验证,声明是验证原理

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

Java IO学习笔记:概念与原理

Java IO学习笔记:概念与原理 一.概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了流,然后将这些流还可以写到另外的目的地(文件.内存.控制台.网络),之所以称为流,是因为这个数据序列在不同时刻所操作的是源的不同部分. 二.分类 流的分类,Java的流分类比较丰富,刚接触的人看了后会感觉很晕.流分类的方式很多: 1.按照输入的方向分,输入流和输出流,输入输出的参照对象是Java程序. 2.