2

在 Visual Studio 2012 附带的 MS IDL 版本中,对 WinRT 的支持添加了这样的结构:

        [activatable(Windows.Networking.Sockets.IControlChannelTriggerFactory,
                     0x06020000)]
        [threading(mta)]
        [marshaling_behavior(agile)]
        [version(0x06020000)]
        runtimeclass ControlChannelTrigger
        {
            [default] interface Windows.Networking.Sockets.IControlChannelTrigger;
            interface Windows.Foundation.IClosable;
        }

我正在使用 IMetaDataImport 分析 winmd 文件中的所有类型。如何找出“运行时类”实现了哪些接口,以及默认接口是哪个接口?

4

1 回答 1

5

如何找出“运行时类”实现的接口?

Windows 元数据文件由一个关系数据库组成:它基本上是一组相互关联的表。您可以在ECMA 335的 Partition II 中找到逻辑模式的规范。

每个运行时类和接口都由元数据数据库的 TypeDef 表中的一行表示。“类型 X 实现接口 I 和 J”关系由 InterfaceImpl 表中的行表示,该表将类型定义映射到它们实现的接口。

事实证明,由于多种原因,计算由一个类型实现的接口集是相当困难的。假设我们要计算由 XAMLButton控件实现的接口集。以下所有步骤都是必需的:

  1. 查找并加载定义Button. 你可以通过调用来做到这一点RoGetMetaDataFile

  2. 查找该Button类型的元数据标记。这实际上是该类型的唯一标识符。你可以通过调用得到这个FindTypeDefByName

  3. 我们需要计算Button类型直接实现的所有接口。这可以通过调用来完成EnumInterfaceImpls

  4. Button类型派生自类型ButtonBase。这是由 TypeDef 表的extends字段指定的,您可以通过调用GetTypeDefProps. 我们需要计算它实现的所有接口。然后我们需要在类层次结构上一直做同样的事情——因为Button,这是很多类型: ContentControlControlFrameworkElementUIElementDependencyObject

    请注意,其中一些类型可能在另一个元数据文件中定义。在这种情况下,InterfaceImpl 将通过 TypeRef 令牌而不是 TypeDef 令牌来引用接口。TypeRef 表包含对类型的引用。您需要解析该引用,加载正确的元数据文件,并在该元数据文件中找到目标类型。这可以使用我们已经为Button.

  5. 每个接口也可能实现其他接口,因此对于我们迄今为止在列表中积累的每个接口,我们需要获取该接口所需的接口集。这是通过递归获取每个接口的 InterfaceImpls 来完成的。

    请注意,就像基类的情况一样,实现的接口也可以在另一个元数据文件中定义。因此,您还需要使用与基类相同的过程来解决这些问题。

    对于一个额外的挑战,Windows 运行时支持通用接口。如果一个接口实现了一个实例化的通用接口,则该实例化接口将由 TypeSpec 表中的一行表示,并伴随着 blob 流中的签名。您将需要解析签名。执行此操作的过程相当复杂,但数据格式完全在 ECMA 335 中指定。

此时,您将拥有该类型实现的全套接口。可以通过检查与Button类型直接关联的 InterfaceImpl 找到默认接口:默认接口将DefaultAttribute应用到它(请注意,自定义属性应用于 InterfaceImpl,而不是接口类型)。您可以使用 枚举每个 InterfaceImpl 的自定义属性EnumCustomAttributes

因此,这是一项非常繁重的工作,并且需要大量代码。我发现该IMetaDataImport界面及其朋友处理起来相当不友好,因此我为 Boost 许可的CxxReflect 库编写了自己的界面。CxxReflect 元数据库提供比签名更好的类型检查,IMetaDataImport并包含解析签名所需的逻辑。它没有那么快IMetaDataImport,但它正在接近。

CxxReflect 反射库包含用于跨元数据文件边界解析类型以及与 Windows 运行时集成的大部分逻辑。它仍处于 alpha 质量阶段,但它对于常见任务非常有效(它目前正在进行一些重构,以使类型解析逻辑可重用于反射以外的用途)。

您当然可以自己实现逻辑来执行此操作,但这非常乏味。在 CxxReflect 的开发过程中,我发现了另一个我没有考虑的极端情况,不得不进行大量的返工。规范是完整的,但事情应该如何工作并不总是很明显。

于 2012-10-15T18:51:39.620 回答