在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文件中的所有类型。如何找出“运行时类”实现的接口,以及默认接口的接口是什么?
答案 0 :(得分:5)
如何找出“运行时类”实现的接口?
Windows元数据文件由关系数据库组成:它基本上是一组相互关联的表。您可以在ECMA 335的分区II中找到逻辑架构的规范。
每个运行时类和接口都由元数据数据库的TypeDef表中的一行表示。 “类型X实现接口I和J”关系由InterfaceImpl表中的行表示,它将类型定义映射到它们实现的接口。
由于多种原因,计算由类型实现的接口集非常困难。假设我们想要计算由XAML Button
控件实现的接口集。需要执行以下所有步骤:
查找并加载定义Button
的元数据文件。您可以致电RoGetMetaDataFile
。
查找Button
类型的元数据标记。这实际上是该类型的唯一标识符。您可以致电FindTypeDefByName
。
我们需要计算Button
类型直接实现的所有接口。这可以通过致电EnumInterfaceImpls
。
Button
类型来自ButtonBase
类型。这由TypeDef表的 extends 字段指定,您可以通过调用GetTypeDefProps
获取该字段。我们需要计算它实现的所有接口。然后我们需要在类层次结构中一直这样做 - 对于Button
,这有很多类型:ContentControl
,Control
,FrameworkElement
,{{ 1}}和UIElement
。
请注意,其中一些类型可能在另一个元数据文件中定义。在这种情况下,InterfaceImpl将通过TypeRef令牌引用接口,而不是TypeDef令牌。 TypeRef表包含对类型的引用。您需要解析该引用,加载正确的元数据文件,并在该元数据文件中查找目标类型。这可以使用我们为DependencyObject
执行的步骤来完成。
每个接口也可以实现其他接口,因此对于我们目前在列表中累积的每个接口,我们需要获取接口所需的接口集。这是通过递归获取每个接口的InterfaceImpls来完成的。
请注意,与基类的情况一样,实现的接口也可以在另一个元数据文件中定义。因此,您还需要使用与基类相同的过程来解决这些问题。
对于增加的挑战,Windows运行时支持通用接口。如果接口实现实例化的通用接口,则该实例化的接口将由TypeSpec表中的行表示,并且将在blob流中伴随签名。您需要解析签名。执行此操作的过程相当复杂,但数据格式在ECMA 335中完全指定。
此时,您将拥有该类型实现的完整接口集。通过检查与Button
类型直接关联的InterfaceImpls可以找到默认接口:默认接口将应用DefaultAttribute
(请注意,自定义属性应用于InterfaceImpl,而不是接口类型)。您可以使用EnumCustomAttributes
枚举每个InterfaceImpl的自定义属性。
所以,这是一项非常多的工作,而且需要大量的代码。我发现Button
界面及其朋友处理起来相当不友好,所以我为Boost许可的CxxReflect library编写了自己的界面。 CxxReflect元数据库提供比IMetaDataImport
更好的类型检查,并包括解析签名所需的逻辑。它不如IMetaDataImport
快,但它已经接近了。
CxxReflect Reflection库包含用于跨元数据文件边界解析类型以及与Windows运行时集成的大部分逻辑。它仍处于alpha质量阶段,但它对于常见任务非常有效(它目前正在进行一些重构,以使类型解析逻辑可以重复用于反射以外的用途)。
你当然可以自己实现逻辑,但这非常繁琐。在CxxReflect的开发过程中,我发现了几次我没有考虑到的另一个角落案例,我不得不做大量的返工。规范已经完成,但事情并不总是很明显。