How to you get the interface identifier (IID) for an interface from a *.winmd
file when using IMetadataImport?
e.g. Windows.Globalization.ICalendar: {CA30221D-86D9-40FB-A26B-D44EB7CF08EA}
A good example is Windows.Globalization.ICalendar interface. Its IID is CA30221D-86D9-40FB-A26B-D44EB7CF08EA
.
You can find it in the source Windows.Globalization.idl
file:
[exclusiveto(Windows.Globalization.Calendar)]
[uuid(CA30221D-86D9-40FB-A26B-D44EB7CF08EA)]
[version(0x06020000)]
interface ICalendar : IInspectable
{
//...snip...
}
Reminder: You're not supposed to parse these files. It gets compiled into a *.winmd
assembly, and that database is the ground-truth.
You can find it in the windows.globalization.h
file, which was generated from the *.winmd
using an import tool:
namespace ABI {
namespace Windows {
namespace Globalization {
MIDL_INTERFACE("CA30221D-86D9-40FB-A26B-D44EB7CF08EA")
ICalendar : public IInspectable
{
//...snip...
}
You can even find the InterfaceID in the resulting compiled *.winmd
assembly database:
But how do I get it when using the documented IMetadataImporter
API?
The abridged version of how to get up and running reading winmd
metadata files:
// Create your metadata dispenser:
IMetadataDispsener dispener;
MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);
//Open the winmd file we want to dump
String filename = "C:\Windows\System32\WinMetadata\Windows.Globalization.winmd";
IMetaDataImport reader; //IMetadataImport2 supports generics
dispenser.OpenScope(filename, ofRead, IMetaDataImport, out reader); //"Import" is used to read metadata. "Emit" is used to write metadata.
The custom attribute blob is the C# serialized format of a Guid class:
3.2.2 DefineCustomAttribute
The format of pBlob for defining a custom attribute is defined in later in this spec. (broadly speaking, the blob records the argument values to the class constructor, together with zero or more values for named fields/properites – in other words, the information needed to instantiate the object specified at the time the metadata was emitted). If the constructor requires no arguments, then there is no need to provide a blob argument.
4.3.6 GetCustomAttributeProps
A custom attribute is stored as a blob whose format is understood by the metadata engine, and by Reflection; essentially a list of argument values to a constructor method which will create an instance of the custom attribute.
In order to get the GuidAttriute guid value, you have to emulate C#'s deserializing a Guid object from a stream.
Starting with your IMetadataImport you call IMetaDataImport.GetCustomAttributeByName.
The first tricky part is figuring out the name of the attribute I'm after. I know it's Guid
when viewed in IDL or C#:
[Guid("CA30221D-86D9-40FB-A26B-D44EB7CF08EA")]
interface ICalendar
{
//...
}
And that underneath it would actually be called "GuidAttribute"
. But neither of those actually work:
"Guid"
: Fails with S_FALSE
"GuidAttribute"
: Fails with S_FALSE
You might try the full name of the attribute class:
"System.Runtime.InteropServices.GuidAttribute"
But that also fails because that's the name of the GuidAttribute class in the .NET framework. In the WinRT library you have to use "Windows.Foundation.Metadata.GuidAttribute"
:
"Guid"
: Fails with S_FALSE
"GuidAttribute"
: Fails with S_FALSE
"System.Runtime.InteropServices.GuidAttribute"
: Fails with S_FALSE
(CLR only)"Windows.Foundation.Metadata.GuidAttribute"
: WorksNow that we figured out the name of the attribute to find, we can query for it:
mdToken calendarTokenID = 0x02000022; //Windows.Globalization.ICalendar
String attributeName = "Windows.Foundation.Metadata.GuidAttribute";
Pointer blob;
UInt32 blobLen;
reader.GetCustomAttributeByName(calendarTokenID, attributeName, out blob, out blobLen);
The next tricky part is is decoding the blob.
Custom attributes each have a different serialization formats. The blob is essentially passed to the Attribute's constructor. The serialization format is the same as the C# serialization format.
For GuidAttribute attributes, the binary serialization format is 20-bytes:
01 00 Prolog (2-bytes) 0x0001 ==> version 1
1D 22 30 CA D9 86 FB 40 A2 6B D4 4E B7 CF 08 EA Guid (16-bytes) "CA30221D-86D9-40FB-A26B-D44EB7CF08EA"
00 00 Trailing null (2-bytes)
The easiest way for me to extract the Guid is to declare a matching structure, cast the returned pointer to a type of that structure, and access the Guid member:
struct SerializedGuidAttribute
{
UInt16 prolog; //2-bytes. 0x0001
Guid guid; //16-byte guid
UInt16 footer; //2-byte footer
}
typedef SerializedGuidAttribute* PSerializedGuidAttribute;
Guid guidAttriute = PSerializedGuidAttribute(blob).guid;
Guid GetGuidAttribute(IMetadataReader reader, mdToken intf)
{
Pointer blob;
UInt32 blobLen;
reader.GetCustomAttributeByName(intf, "Windows.Foundation.Metadata.GuidAttribute",
out blob, out blobLen);
//if (blobLen != 20) { throw new Exception("Something") };
return PSerializedGuidAttribute(blob).guid;
}