Как получить идентификатор интерфейса (IID, то есть GUID) интерфейса при импорте WinRT WinRT?

Укороченная версия

Как получить идентификатор интерфейса (IID) для интерфейса от *.winmd файл при использовании IMetadataImport?

например, Windows.Globalization.ICalendar: {CA30221D-86D9-40FB-A26B-D44EB7CF08EA}

Более длинная версия

Хорошим примером является интерфейс Windows.Globalization.ICalendar. Это IID CA30221D-86D9-40FB-A26B-D44EB7CF08EA,

Это в IDL

Вы можете найти это в источнике Windows.Globalization.idl файл:

[exclusiveto(Windows.Globalization.Calendar)]
[uuid(CA30221D-86D9-40FB-A26B-D44EB7CF08EA)]
[version(0x06020000)]
interface ICalendar : IInspectable
{
   //...snip...
}

Напоминание: вы не должны анализировать эти файлы. Это компилируется в *.winmd сборка, и эта база данных - основа правды.

Это в шапке

Вы можете найти его в windows.globalization.h файл, который был сгенерирован из *.winmd используя инструмент импорта:

namespace ABI {
    namespace Windows {
        namespace Globalization {

            MIDL_INTERFACE("CA30221D-86D9-40FB-A26B-D44EB7CF08EA")
            ICalendar : public IInspectable
            {
               //...snip...
            }

Это даже в winmd

Вы даже можете найти InterfaceID в скомпилированном результате *.winmd сборочная база данных:

Но как мне это получить при использовании документированного IMetadataImporter API?

Код

Сокращенная версия о том, как начать чтение winmd файлы метаданных:

// 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.

Бонус Чтение

  • Блоги MSDN: Metadata Unmanaged API (предварительная версия PDF старого документа Word, которая, насколько я могу судить, является единственной документацией Microsoft для API метаданных) ( архив)

1 ответ

Решение

Начиная с IMetadataImport, вы вызываете IMetaDataImport.GetCustomAttributeByName.

Первая сложная часть - выяснить имя атрибута, который я ищу. Я знаю это Guid при просмотре в IDL или C#:

[Guid("CA30221D-86D9-40FB-A26B-D44EB7CF08EA")]
interface ICalendar
{
    //...
}

И что под ним на самом деле будет называться "GuidAttribute", Но ни один из них на самом деле не работает

  • "Guid" С ошибками S_FALSE
  • "GuidAttribute" С ошибками S_FALSE

Вы можете попробовать полное имя класса атрибута:

  • "System.Runtime.InteropServices.GuidAttribute"

Но это также не помогает, потому что это имя класса GuidAttribute в.NET Framework. В библиотеке WinRT вы должны использовать "Windows.Foundation.Metadata.GuidAttribute":

  • "Guid" С ошибками S_FALSE
  • "GuidAttribute" С ошибками S_FALSE
  • "System.Runtime.InteropServices.GuidAttribute" С ошибками S_FALSE (Только CLR)
  • "Windows.Foundation.Metadata.GuidAttribute" С ошибками S_FALSE (Только WinRT)

Теперь, когда мы выяснили имя атрибута для поиска, мы можем запросить его:

mdToken calendarTokenID = 0x02000022; //Windows.Globalization.ICalendar
String  attributeName   = "Windows.Foundation.Metadata.GuidAttribute";

Pointer blob;
UInt32 blobLen;
reader.GetCustomAttributeByName(calendarTokenID, attributeName, out blob, out blobLen);

Следующая сложная часть - это расшифровка капли.

Расшифровка капли

Каждый из пользовательских атрибутов имеет разные форматы сериализации. BLOB-объект по существу передается конструктору Атрибута. Формат сериализации такой же, как и в формате сериализации C#.

Для атрибутов GuidAttribute двоичный формат сериализации составляет 20 байтов:

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)

Самый простой способ для меня извлечь Guid - объявить соответствующую структуру, привести возвращенный указатель к типу этой структуры и получить доступ к члену Guid:

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;
}
Другие вопросы по тегам