10.COM进程外组件和列集、散集

前面讲的都是进程内组件,实际上COM是进程透明的,就是使用COM的时候,不管当前连接的是进程内组件还是进程外组件,使用方法一样,所有的差异都被COM中间屏蔽了。这一节详细讲解COM进程外组件的列集/散集过程和连接建立过程。

1.基本概念

怎么区分当前连接的COM组件是进程内合适进程外呢,实际上致命前面调用的函数CoCreateInstance参数即可,支持的参数和对应的DLL(exe)位置如下:
    CLSCTX_INPROC_SERVER 连接进程内组件 
    CLSCTX_LOCAL_SERVER 连接本机进程外组件
    CLSCTX_REMOTE_SERVER 连接远程主机上组件
这里可使用|连接多个值,COM会自动找到最优的组件查找路径
CoCreateInstance调用时,客户感知不到具体连接过程,使用方法和进程内组件一致(进程透明)

连接远程组件示意图如下:

这里客户和组件具体通信,依赖代理和存根,这两者也是COM对象。现在需要关注两个问题:
  • 如何建立连接
  • 如何传递参数

2.如何传递参数

因为建立连接依赖传递参数的方法,所以先说如何传递参数。
COM中传递参数的过程是一个打包/拆包的过程, 打包叫做列集,拆包的过程叫散集,具体的数据以流的形式传递,这里的流可以通过COM的IStream接口得到,其实就是带 前缀长度和类型的结构体
先看下面一幅图:

这里有三种典型的数据传递:
【1.】普通数据(int) 
直接打包传递即可
【2.】指针数据(int*)
打包实际指针的数据,在客户端拆包时生成同样的数据,将新数据的指针给客户
【3.】接口数据(ICatSub*)
将对应接口GUID(IID_CAT)传递给存根,这样在存根创建出对应的接口,实际通信过程有接口代理ICatProxy和接口存根ICatSub完成,也就是完成了连接的过程

这里实际列集和散集使用函数CoMarshalInterface/CoUnmarShalInterface,

列集函数原型如下:
HRESULT CoMarshalInterface(
  _In_     LPSTREAM  pStm,
  _In_     REFIID    riid,
  _In_     LPUNKNOWN pUnk,
  _In_     DWORD     dwDestContext,
  _In_opt_ LPVOID    pvDestContext,
  _In_     DWORD     mshlflags
);
该函数可以将指定riid的接口pUnk数据列集到pStm流中,这里pvDestContext保留=0即可。
dwDestContext 表示传输距离如下,可设置超过实际使用距离,com自动优化:
MSHCTX_INPROC 进程内
MSHCTX_LOCAL 同主机进程外
MSHCTX_DIFFRENTMACHINE不同主机
mshlFlag 表示列集/散集语义如下:
MSHLFLAGS_NORMAL 散集马上进行,数据是临时的,列集一次,散集一次
MSHLFLAGS_TABLESTRONG 散集可能延迟列集一次,散集多次

散集函数原型如下:
HRESULT CoUnmarshalInterface(
  _In_  LPSTREAM pStm,
  _In_  REFIID   riid,
  _Out_ LPVOID   *ppv
);
从指定数据流中散列出指定riid的接口数据

列集分 自定义列集和标准列集法
调用CoMarshalInterface/CoUnmarShalInterface时,如果对象自己实现了IMarshal接口,则此时采用对象自定义列集,否则采用COM提供的标准列集对象CLSID_StdMarshal。除非是为了优化效率,一般都直接采用标准列集,此时COM帮我们做好了参数列集散集和通信等过程,我们只需要直接填充列集代码即可,借助后文的IDL,这一过程也可以借助工具完成。

3.如何建立连接

前面说接口列集就是连接建立的过程,所以这里先讲自定义列集法列集建立连接过程再讲标准列集建立连接过程。

a.自定义列集连接



a.类厂对象是第一个跨进程的连接,

1.在我们客户端调用CoCreateInstance创建远程组件接口时,首先CoGetClassObject中请求类厂代理/存根的连接
2.客户端向组件请求类厂对象,组件创建类厂对象,并调用CoRegisterClassObject向COM注册,在这个函数中会列集类厂对象,创建类厂存根对象
3.通过存根对象返回类厂代理CLSID等连接信息,散集后,根据相关信息创建类厂代理对象,建立类厂代理/存根连接

b.获取实际接口都是通过类厂代理/存根,具体过程如下:

4.pFactory->CreateInstance创建指定CLSID接口时,类厂代理对象中列集接口CLSID等信息,传递给类厂存根对象
5.类厂存根对象根据传递来的数据散集后的信息创建对应的组件对象
6、7.类厂存根对象中将创建的组件接口再列集回传给类厂代理对象,此时创建接口存根对象,通过此接口列集
8、9.类厂代理对象中散集接收到的数据,并创建接口代理对象,接口代理指针返回给客户

由此可见, 连接其实就是建立代理/存根之间的连接,后续参数的列集/散列和函数调用都是通过这个连接进行

b.标准列集连接



标准列集其实是自定义列集的特例,它是以接口为单位列集的,代理管理器和存根管理器分别聚合不同接口的代理和存根,实际通信时找到对应的接口即可。代理和存根之间通过RPC通信。详细过程可参见《COM原理与应用》,这里不再赘述。

采用标准列集建立连接后,我们只需要实现每个接口的代理/存根模块,一旦系统中安装了某个接口的代理/存根并正确的注册,则代理管理器/存根管理器会在需要的时候自动加载接口代理和接口存根,从实现角度讲, 标准列集时我们需要做的就是针对接口实现代理、存根程序
进程外组件实现多个接口时,如果是COM/OLE标准接口,则可以直接使用COM/OLE提供的标准实现,如果是自定义的接口,则必须自己实现代理/存根并注册到系统中,才能真正使用这些接口。OLE提供给了很多常用的接口,可以选择使用这些接口,以避免实现代理/存根。
标准列集的代理存根在同一个dll

微软提供了MIDL工具辅助实现编写自定义代理/存根程序,下一节讲IDL的使用之后演示如何使用IDL编写远程组件和实现自定义代理/存根。


原创,转载请注明来自 http://blog.csdn.net/wenzhou1219
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
进程和线程是操作系统中的两个重要概念,它们在并发执行任务和资源管理方面起着关键作用。下面是进程和线程的区别和联系: 区别: 1. 定义:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;线程是进程中的一个执行单元,是CPU调度和执行的基本单位。 2. 资源占用:进程拥有独立的地址空间和系统资源,如文件描述符、内存等;线程共享所属进程的资源,包括代码段、数据段和打开的文件等。 3. 切换开销:进程切换需要保存和恢复整个进程的上下文,开销较大;线程切换只需要保存和恢复线程的上下文,开销较小。 4. 通信方式:进程间通信需要使用操作系统提供的机制,如管道、消息队列、共享内存等;线程间通信可以直接读写同一进程的共享变量。 联系: 1. 关系:一个进程可以包含多个线程,线程是进程的执行单元。 2. 共享资源:线程共享所属进程的资源,可以方便地共享数据和通信。 3. 并发执行:多个线程可以在同一进程中并发执行,提高了程序的执行效率。 总结: 进程和线程在操作系统中扮演不同的角色,进程是资源分配和调度的基本单位,线程是执行和调度的基本单位。进程拥有独立的资源,线程共享所属进程的资源。进程切换开销较大,线程切换开销较小。进程间通信需要使用操作系统提供的机制,线程间通信可以直接读写共享变量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值