Short version
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}
Longer Version
A good example is Windows.Globalization.ICalendar interface. Its IID is CA30221D-86D9-40FB-A26B-D44EB7CF08EA
.
It's in the IDL
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.
It's in the header
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...
}
It's even in the winmd
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?
Code
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.
Bonus Reading
- MSDN Blogs: Metadata Unmanaged API (a preliminary PDF version of an old Word document that, as far as i can tell, is the only Microsoft documentation for the Metadata API) (archive)
Short Version
The custom attribute blob is the C# serialized format of a Guid class:
In order to get the GuidAttriute guid value, you have to emulate C#'s deserializing a Guid object from a stream.
Long Version
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#:And that underneath it would actually be called
"GuidAttribute"
. But neither of those actually work:"Guid"
: Fails withS_FALSE
"GuidAttribute"
: Fails withS_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 withS_FALSE
"GuidAttribute"
: Fails withS_FALSE
"System.Runtime.InteropServices.GuidAttribute"
: Fails withS_FALSE
(CLR only)"Windows.Foundation.Metadata.GuidAttribute"
: WorksNow that we figured out the name of the attribute to find, we can query for it:
The next tricky part is is decoding the blob.
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:
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:
And you have it
Bonus