【问题标题】:IMetaDataImport and WinRT runtimeclass interfacesIMetaDataImport 和 WinRT 运行时类接口
【发布时间】:2012-10-16 02:51:39
【问题描述】:

在 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 文件中的所有类型。如何找出“运行时类”实现了哪些接口,以及默认接口是哪个接口?

【问题讨论】:

    标签: reflection windows-runtime idl


    【解决方案1】:

    如何找出“运行时类”实现了哪些接口?

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

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

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

    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 类型直接关联的 InterfaceImpls 找到:默认接口将应用DefaultAttribute(请注意,自定义属性应用于 InterfaceImpl,而不是接口类型) .您可以使用EnumCustomAttributes 枚举每个 InterfaceImpl 的自定义属性。

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

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

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

    【讨论】:

    • 顺便说一句,带有DefaultAttribute的接口将是RuntimeClass用作API参数时使用的接口。
    • 感谢:EnumInterfaceImpls 实际上是我所追求的。就我而言,所有其他的继承情况都有望自然而然地消失,因为它都将映射到多重继承(在 Python 中)。
    • @Martinv.Löwis:您正在构建 Python 语言投影吗?
    • @Martinv.Löwis:太好了!听起来很有趣。 :-)
    猜你喜欢
    • 2013-01-04
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-26
    • 1970-01-01
    • 2019-07-13
    • 2015-10-02
    相关资源
    最近更新 更多