C++ 为什么COM接口契约是不可变的?

C++ 为什么COM接口契约是不可变的?,c++,com,C++,Com,我在谷歌上搜索了不少,发现没人愿意解释为什么COM接口是不可变的,这很奇怪。我认为您无法从COM接口中删除任何方法的原因是,依赖该接口的客户端将遇到错误,这是不好的。但为什么向界面添加新功能会改变这一切呢?这与基础vtable有关吗?如果添加方法,则使用该方法的较新客户端在使用较旧版本的组件时将失败 旧版本的组件不会有新方法,除非您专门添加代码来实现它,重新生成组件,然后在使用该组件的所有计算机上重新安装该组件 如果较新版本的客户端尝试在没有该方法的较旧版本的组件上调用新方法,则会发生未定义的行

我在谷歌上搜索了不少,发现没人愿意解释为什么COM接口是不可变的,这很奇怪。我认为您无法从COM接口中删除任何方法的原因是,依赖该接口的客户端将遇到错误,这是不好的。但为什么向界面添加新功能会改变这一切呢?这与基础vtable有关吗?

如果添加方法,则使用该方法的较新客户端在使用较旧版本的组件时将失败

旧版本的组件不会有新方法,除非您专门添加代码来实现它,重新生成组件,然后在使用该组件的所有计算机上重新安装该组件

如果较新版本的客户端尝试在没有该方法的较旧版本的组件上调用新方法,则会发生未定义的行为(可能会发生崩溃,但也可能发生静默数据损坏)。新客户端将试图通过vtable中的指针项调用方法,而在构建旧客户端时,vtable中不存在指针项,因此旧客户端在此位置将具有一些不相关的值


当然,如果您同时控制客户端和组件并将它们部署在一起,这对您来说不会是一个问题,但是COM是为更广泛的用例而设计的

COM有一个非常严重的DLL地狱问题。几个基本原因:

  • 参与编写服务器和客户机代码的程序员很少相互了解,他们不一起工作,并且有自己的发布时间表
  • 默认情况下,注册服务器是在计算机范围内进行的,这会影响依赖于服务器的每个客户端程序。带有无注册清单的隔离COM是一种解决方法
  • 早期绑定COM(使用v表)非常有效,但对v表更改极不容忍。当客户端代码调用完全错误的函数或传递错误的参数时,很难诊断不匹配。通过IDispatch进行的延迟绑定调用是一种解决方法,但速度较慢
  • COM程序员有很强的作弊动机,更改接口{guids}会导致客户机程序员脾气暴躁,支持调用笨拙。使接口向后兼容相对容易,但使其向前兼容永远不起作用。只有更改接口guid才是真正安全的
  • COM服务器的部署通常是客户机的职责,他们通常对服务器了解不够,无法排除故障并纠正问题

这些都是一般的版本控制问题,许多运行时实现都会遭受各种各样的痛苦。COM的一个特殊优势是您可以对此做些什么。更改{guids}会让很多不好的东西消失。

合同是合同,是合同……当然,但是向接口添加新方法如何破坏使用COM组件的客户端?您必须提供额外的接口。COM合同是固定的,如果不破坏您的客户机,则无法扩展。这是IDL编译器的本质。难道“旧版本的组件”就不会因为接口的本质而实现这些方法吗?或者是因为我们有一些COM仍然在运行,自从接口更新后还没有重新启动,因此缺少方法?不知道为什么有人否决了我的答案。这是对问题主体中所问问题的正确答案。@Amnestic,呃?如果我使用你的接口创建了一个DLL,然后你更新了你的接口,那么我的DLL不会神奇地更新以实现新功能!试图调用新函数的代码只会在vtable结束后读取内存中的任何垃圾,因为我的vtable比“新”接口规范要求的要短。@DavidAnderson有些人不喜欢一句话的答案;也许你可以详细解释一下。我不知道为什么我很难理解,显然我们可以让一个客户使用一个旧的COM和一个新的接口。我只是个白痴。“改变{guids}很多肮脏的东西就会蒸发掉。”。我同意。我发现您可以通过对C++接口类进行模板化来使COM合同接口可变。我现在要做的就是更改我的IID\u T、CLSID\u T和LIBID\u T GUID模板参数,并添加或更改任何我想要区分相关项目的代码。我不仅获得了轻松更改guid的能力,而且还获得了模板化代码重用的好处。您所得到的是让接口向不同方向迁移的能力,而不会绊倒以前发布的版本。