在现代软件开发中,跨语言的组件交互变得越来越重要。C++WinRT组件与C#.NETMetro 应用的交互便是一个典型的例子。本文将详细探讨如何通过 RCW(Runtime Callable Wrapper)机制使 C# 应用访问 C++ WinRT 组件,并分析 Visual C++ 定义的组件扩展以及 ABI(Application Binary Interface)接口的实现。
首先,需要创建一个 C++ WinRT组件。在 Visual Studio 中,这可以通过创建一个 WinRT 组件项目来完成。以下是 C# 中的一个简单示例,展示了如何定义一个 CalculatorSample 类,该类提供了加、减、乘三种基本运算功能。
#pragma once
using namespace Windows::Foundation;
namespace CppWinRTComponentDll {
public ref class CalculatorSample sealed {
public:
int Add(int x, int y);
int Sub(int x, int y);
int Mul(int x, int y);
};
}
在这个示例中,可以看到一些非标准C++的关键字。Visual C++ 定义了一些组件扩展,这些扩展在 Metro 风格的应用中,本质上是对底层 COM 调用的语法糖。这些扩展通常用于公共接口,其中代码在 ABI 之间传递 Windows Runtime 类型到 JavaScript、C# 或 Visual Basic(甚至是另一个 C++ 模块)。Visual C++ 提供了 Windows Runtime 类型和标准 C++ 类型之间的隐式转换。典型的模式是内部使用标准 C++ 类型和库,而在公共接口中转换为 Windows Runtime 类型。
当构建 CppWinRTComponentDll 项目时,可以在输出窗口中看到以下内容。注意高亮显示的 CppWinRTComponentDll.winmd 文本。这意味着编译器正在生成一个 windmd 类型的文件。
在 Debug 文件夹中,可以找到 CppWinRTComponentDll.winmd 文件,如图 1 所示。这个 WinMD 文件本质上是一个 CLI 元数据文件,可以使用 ILDASM 打开,如图 2 所示。
CppWinRTComponentDll 通过 CppWinRTComponentDll.winmd 文件中的 API 元数据暴露。WinMD 文件的格式是 Ecma-335,这与.NETFramework 使用的格式相同。它只是使用了 ECMA-335 文件格式标准 & amp ; 框架,没有更多。底层的二进制合约使能够直接在开发语言中访问 CppWinRTComponentDll API。CppWinRTComponentDll API 的形状和结构可以被静态语言(如C#)和动态语言(如 JavaScript)理解。在 JavaScript、C#、Visual Basic 和 C++ 中都可以使用 IntelliSense,如图 3 所示。
CppWinRTComponentDll.winmd 文件是由 C++ 编译器创建的,可以看作是一个跨语言的头文件,因为C#不能理解 C++ 头文件,JavaScript 也不能。所以需要一个所有语言都能理解的文件格式,那就是 .winmd 文件格式。尽管定义了一个 CalculatorSample 类,但编译器为创建了一个 __ICalculatorSamplePublicNonVirtuals 接口,如图 4 所示,这是与计算器类通信的 COM 接口。这增加了它的公共表面。
CppWinRTComponentDll.winmd 文件的清单可以在图 5 中看到。
在 ILDASM 中,点击 .class 接口 private abstract auto ansi windowsruntime。这个 GUID 属性就像指定了接口的 GUID,如图 6 所示。这是组件的 GUID。
WinMD 文件不包含任何 IL。这就像一个头信息,以便其他语言可以理解。这使得投影发生。计算器是一个 ref 类本身。CppWinRTComponentDll.CalculatorSample 类实现了 __ICalculatorSamplePublicNonVirtuals 接口,并实现了 Add、Sub 和 Mul 方法,如图 7 所示。
注意 WinRT 中的每个对象都是引用计数的。