对象间通信

COM 旨在允许客户端以透明方式与对象进行通信,而不管这些对象在同一进程中、同一台计算机或不同计算机上的何处运行。 这为所有类型的对象以及对象客户端和对象服务器提供单个编程模型。

从客户端的角度来看,所有对象都通过接口指针进行访问。 指针必须是进程内指针。 事实上,任何接口函数调用始终会先到达一些进程内代码块。 如果对象在进程内,则调用会直接到达该对象,无需插入系统基础结构代码。 如果对象在进程外,则调用首先到达 COM 或对象提供的称为“proxy”的对象(根据实现者的期望)。 代理包调用参数(包括任何接口指针),并为对象实现所在的其他进程或其他计算机生成相应的远程过程调用(或自定义生成代理时的其他通信机制)。 这一用于跨进程边界传输的打包指针的过程称为封送

从服务器的角度来看,所有对象的接口函数调用都通过指向该接口的指针进行。 同样,指针仅在单个进程中具有上下文,调用方必须始终是某个进程内代码块。 如果对象在进程内,则调用方是客户端本身。 否则,调用方是 COM 或对象本身提供的“存根”对象。 存根从客户端进程中的“代理”接收远程过程调用(或自定义生成代理时的其他通信机制),拆收参数,并调用服务器对象的相应接口。 从客户端和服务器的角度来看,它们始终直接与一些其他进程内代码通信。

COM 提供封送实现,称为标准封送。 此实现非常适合大多数对象,大大减少了编程要求,让封送过程变得有效透明。

但是,在一些情况下,接口与 COM 进程透明度实现之间存在的明显差距可能会产生一些阻碍。 从客户端的角度来看,侧重于功能的接口设计有时会导致设计决策与跨网络有效实现该接口存在冲突。 在这种情况下,不需要完全保持进程透明度,而只需“关注进程透明度”。COM 通过允许对象实现器支持自定义封送处理(也称为 IMarshal 封送)来提供此功能。 标准封送实际上是自定义封送的一个实例,是对象不需要自定义封送时使用的默认实现。

可以实现自定义封送,以允许对象从网络使用时执行不同的操作,而不是在本地访问下执行的操作,并且对客户端完全透明。 借助此体系结构,可以设计客户端/对象接口,无需考虑网络性能问题,以后在不中断既有设计的情况下解决网络性能问题。

COM 不指定组件的构造方式;但指定交互方式。 COM 将组件的内部结构问题留给编程语言和开发环境。 相反,编程环境没有设定在立即应用环境外部处理对象的标准。 例如,Microsoft Visual C++ 特别适合在应用程序中操作对象,但不支持在应用程序外部处理对象。 通常,所有其他编程语言在这方面都一样。 因此,为了提供网络范围的互操作性,COM 通过独立于语言的接口选取编程语言的结束位置。

双间接 vtbl 结构意味着函数指针表中的指针不需要直接指向实际对象中的实际实现。 这是进程保持透明的核心。

对于对象直接加载到客户端进程的进程内服务器,表中的函数指针直接指向实际实现。 在这种情况下,从客户端到接口方法的函数调用会将执行控制直接传输到该方法。 但是,这不适用于本地对象,更不用说远程对象了,因为指向内存的指针不能在进程之间共享。 不过,客户端必须能够调用接口方法,就像调用实际实现一样。 因此,客户端通过调用将控件统一传输到某些对象中的方法。

客户端始终调用一些进程内对象的接口方法。 如果实际对象是本地对象或远程对象,则调用代理对象,然后对实际对象进行远程过程调用。

那么,实际执行哪种方法? 答案是,每当调用进程外接口时,每个接口方法都由代理对象实现。 代理对象始终是一个进程内对象,该对象代表所调用的对象执行操作。 此代理对象知道实际对象在本地或远程服务器中运行。

代理对象打包某些数据包中的函数参数,并生成对本地或远程对象的 RPC 调用。 该数据包由本地或远程计算机上的服务器进程中的存根对象选取,该对象解压缩参数并调用该方法的实际实现。 当该函数返回时,存根会打包任何 out 参数和返回值,并将其发送回代理,这会将其解压缩,并将其返回到原始客户端。

因此,客户端和服务器始终相互通信,一切都会按步就班进行。 来自客户端的所有调用和对服务器的所有调用都在某个时刻进行。 但是,由于 vtbl 结构允许某些代理(如 COM)截获所有函数调用和所有返回的函数,代理可以根据需要将这些调用重定向到 RPC 调用。 尽管进程内调用比进程外调用更快,但是进程差异对客户端和服务器而言完全透明。

有关详情,请参阅以下主题:

COM 客户端和服务器

接口封送