COM是如何实现语言互操作的?

How does COM achieve language interop?

我了解 COM 如何实现与编译器无关的 C++ 代码,因为它通过谨慎使用 C++ 语言的哪些功能来定义 ABI。它只是 C++ 代码以一种非常聪明的方式与 C++ 代码对话。但是我仍然不明白它如何允许与 C# 或 Javascript 进行语言互操作。

边界在哪里?我现在唯一的解释是,语言编译器本身必须对 COM 有特殊的支持,这样它才能生成正确的汇编代码,以允许 caller/callees.

之间的准确通信。

"Type Library" 实现了 COM 组件在不同语言之间的互操作。

https://docs.microsoft.com/en-us/windows/desktop/midl/com-dcom-and-type-libraries

A type library (.tlb) is a binary file that stores information about a COM or DCOM object's properties and methods in a form that is accessible to other applications at runtime. Using a type library, an application or browser can determine which interfaces an object supports, and invoke an object's interface methods. This can occur even if the object and client applications were written in different programming languages. The COM/DCOM run-time environment can also use a type library to provide automatic cross-apartment, cross-process, and cross-machine marshaling for interfaces described in type libraries.

语言互操作的另一种方法(例如 C++ 将对象投影到 Javascript)是 COM 对象可以实现 IDispatch.

这当然不是魔法。

COM 为语言互操作设置规则。这只是一份合同,带有一些有用的工具。每种想要支持 COM 的语言都必​​须找到一种方法来遵守自己的规则。他们都必须以某种方式提供自己的兼容机制。

在 C++ 的情况下,正如您提到的那样,规则似乎是免费提供的,但请注意有一个警告:语言标准没有指定 类 和虚函数的布局和机制。 COM 模仿的方法是虚拟调用(“VTable”)的一种极其常见的实现,COM 遵循 Microsoft 编译器使用的确切布局。但是您可以拥有一个完全有效的 C++ 编译器,其中带有虚函数的 类 与 COM 布局不兼容。只是没有人这样做,至少在 Windows 编译器中没有。因此,即使在 C++ 中,编译器也存在一些“中间相遇”。

在 C 中,您必须手动完成所有操作。其他语言可能允许您做同样的事情(当然是汇编语言)。

为了帮助编译语言交换有关特定契约的信息,COM 提供了类型库和读取它们的机制。想要利用它们的编译器或语言也必须“在中间相遇”并学习如何处理它们(例如,Microsoft C++ #import 指令;VB6 库菜单)。

并非每种语言都会支持您在 COM 中可以做的所有事情,因为有一点(在更隐蔽的功能中),return 投资于实现语言支持并没有成功。每种语言都必须选择自己的局限性。您可以在 COM 中执行很多 VB6 无法执行的操作(阅读 IDL 规范)。

因为在类似脚本的语言中遵循 COM 规则在不切实际和不可能之间,COM 提供了一种更适合动态语言的更高级别的方法(自动化),即使更受限制。但是,想要为 Automation 提供客户端支持的语言实施者必须了解 IDispatch 接口、激活机制以及对其语言适当设施的翻译。想要为创建 COM 服务器提供支持的脚本语言必须更加努力地实现有效的 COM IDispatch 实现和代表用户脚本的独立主机引擎。甚至 VBScript 一开始也无法做到这一点,直到 Microsoft 通过 Windows Scripting Host 添加了对 .SCR 的支持。又是“中途相遇”

一个语言想要同时支持纯COM和Automation,需要加倍努力;对一个的支持不会自动给你对另一个的支持。

对于像 C# 这样的 .NET 语言,大部分工作都是针对本机 COM 和 .NET 运行时内部的自动化完成的,它提供了 COM 可调用包装器 (CCW) 和运行时可调用包装器 (RCW) 的实现与 COM 交互所必需的,以及处理 COM 的引用计数方法与 .NET 的 GC 方法之间的冲突。 Microsoft 在一个地方完成了所有工作,因此各个 .NET 语言设计者不必这样做。

所以,是的,语言实现者必须额外工作才能为语言提供对 COM 的特殊支持:遵循二进制布局规则,在需要时实现转换层,and/or 可能提供工具来读取类型库.

Language Interop 要求双方(调用者和被调用者)在某处“在中间相遇”。 COM 只是一种规范,它为设计人员提供了中间地带,“一个所有人都可以见面的地方”。

既然你已经用 WinRT 标记了你的问题,我假设你是在具体询问 WinRT 语言投影是如何实现的。在这种情况下,所有语言都必须通过某种方式将其自然语言结构映射到 WinRT 定义的 COM ABI。该 ABI 源自 ECMA 335 标准中编码的元数据,并应用特殊规则将抽象元数据转换为具体的 ABI。自然有不同的方法来实现这一点。 CLR 本身已更新以支持 C# 中的 WinRT。 Visual C++ 编译器(遗憾地)更新了语言扩展以通过 C++/CX 支持 WinRT。 C++/WinRT 方法非常不同,因为它只需要一个标准的 C++ 编译器,并且所有关于 WinRT 的知识都是通过一个标准的 C++ 头文件库提供的。其他语言可能采用不同的方法,但最终他们必须就元数据中表示的类型转换为基于 COM 的 ABI 上的对象和虚函数调用的方式达成一致。

虽然目前没有很好地记录此过程,但 C++/WinRT 是仅有的开源语言预测之一,因此对于那些需要了解 WinRT 背后工作原理的人来说,它是一个有用的参考实现。

https://github.com/microsoft/cppwinrt