In the MS IDL version that comes with Visual Studio 2012, support for WinRT added such constructs:
[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;
}
I'm using IMetaDataImport to analyse all types in a winmd file. How can I find out what interfaces a "runtimeclass" implements, and which interface the default interface is?
How can I find out what interfaces a "runtime class" implements?
A Windows Metadata file consists of a relational database: it's basically a set of interrelated tables. You can find the specifications for the logical schema in Partition II of ECMA 335.
Every runtime class and interface is represented by a row in the TypeDef table of a metadata database. The "type X implements interfaces I and J" relationship is represented by rows in the InterfaceImpl table, which maps type definitions to the interfaces they implement.
Computing the set of interfaces that are implemented by a type is quite difficult, it turns out, for a number of reasons. Let's say that we want to compute the set of interfaces implemented by the XAML Button
control. All of the following steps are required:
Find and load the metadata file that defines Button
. You can do this by calling RoGetMetaDataFile
.
Find the metadata token for the Button
type. This is effectively the unique identifier for that type. You can get this by calling FindTypeDefByName
.
We need to compute all of the interfaces implemented directly by the Button
type. This can be done by calling EnumInterfaceImpls
.
The Button
type derives from the ButtonBase
type. This is specified by the extends field of the TypeDef table, which you can get by calling GetTypeDefProps
. We need to compute all of the interfaces that it implements. Then we need to do the same all the way up the class hierarchy--for Button
, this is a lot of types: ContentControl
, Control
, FrameworkElement
, UIElement
, and DependencyObject
.
Note that some of these types may be defined in another metadata file. In that case, the InterfaceImpl will refer to the interface via a TypeRef token, not a TypeDef token. The TypeRef table contains references to types. You need to resolve that reference, load the correct metadata file, and find the target type in that metadata file. This can be done using the steps we performed already for Button
.
Each interface may also implement other interfaces, so for each interface we have accumulated in our list so far, we need to get the set of interfaces that that interface requires. This is done by recursively getting the InterfaceImpls for each of these interfaces.
Note that, just as is the case with the base class, an implemented interface may also be defined in another metadata file. So, you'll need to resolve these as well, using the same process as is used for the base class.
For an added challenge, the Windows Runtime supports generic interfaces. If an interface implements an instantiated generic interface, that instantiated interface will be represented by a row in the TypeSpec table and will be accompanied by a signature in the blob stream. You will need to parse the signature. The process for doing this is rather complex, but the data format is completely specified in ECMA 335.
At this point, you'll have the full set of interfaces implemented by the type. The default interface can be found by examining the InterfaceImpls that are directly associated with the Button
type: the default interface will have the DefaultAttribute
applied to it (note that the custom attribute is applied to the InterfaceImpl, not the interface type). You can enumerate the custom attributes for each InterfaceImpl using EnumCustomAttributes
.
So, it's an awful lot of work, and it requires a lot of code. I found the IMetaDataImport
interface and its friends to be rather unfriendly to deal with, so I wrote my own for the Boost-licensed CxxReflect library. The CxxReflect Metadata library offers better type checking than IMetaDataImport
and includes the logic required to parse signatures. It's not quite as fast as IMetaDataImport
, but it's getting close.
The CxxReflect Reflection library includes most of the logic for resolving types across metadata file boundaries and for integrating with the Windows Runtime. It's still at an alpha quality stage, but it works pretty well for common tasks (it's currently undergoing some refactoring to make the type resolution logic reusable for uses other than reflection).
You can certainly implement logic to do this yourself, but it's very tedious. Several times during development of CxxReflect I found yet another corner case that I hadn't accounted for and had to do substantial rework. The specification is complete, but it is not always obvious how things are supposed to work.