深入理解COM(二)

前言

前文讲到了:

这个图概括了进程内组件和进程外组件的调用流程。

对于进程内组件来说:

DllGetClassObject为dll暴露出来的接口。

对于进程外的组件来说,所有的跨进程操作完全由客户程序的代理对象与组件程序的存根代码,这样就达到了一个效果:客户程序调用接口成员函数就如同调用本进程内的还是一样。

本篇会针对进程外组件,讨论COM是如何实现跨进程。

列集

在讨论之前,我们需要了解列集的概念。

我们再来看一张图,这张图展示了客户程序与组件程序通信的模型图

代理对象用列集手段处理成员函数的参数,将其封装成数据包,通过跨进程数据传输,将其发给组件程序。

组件程序使用存根代码,存根代码使用散集的方式将数据包解译出来,然后再去调用组件对象。

这么一看,列集就是给数据进行编码,散集就是给数据进行解码。

列集分为两种实现方式:

  1. 自定义列集法
  2. 标准列集法

自定义列集法就得我们自己写代码实现

标准列集法是COM库提供的现成的轮子,用就完事了。

列集过程:

  1. 确定类厂对象的代理对象的CLSID,假如没有提供代理对象的CLSID,说明要使用标准列集代理对象。CoRegisterClassObject函数要求类厂对象提供列集数据包,如果类厂对象不能提供,则使用COM提供的标准列集数据包。
  2. COM把类厂对象的代理对象的CLSID和列集数据包传输到客户进程中,CoGetClassObject函数正等待这些数据
  3. 在客户进程中,COM根据传输过来的CLSID创建代理对象,并把列集数据包传给代理对象。

这样就完成了客户进程与组件进程的连接。

步骤1可以由COM提供的API CoMarshalInterface实现,步骤3由CoUnmarshalInterface函数实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
HRESULT CoMarshalInterface(
LPSTREAM pStm,
REFIID riid,
LPUNKNOWN pUnk,
DWORD dwDestContext,
LPVOID pvDestContext,
DWORD mshlflags
);
STDAPI CoUnmarshalInterface(
IStream* pStm,
REFIID riid,
void** ppv
);

关于进程外组件,就先到这。

评论