Как получить значение перечисления при использовании IMetadataImport
Укороченная версия
Как получить числовое значение, связанное с перечислением, из *.winmd
файл при использовании IMetadataImport?
Хорошим примером является перечисление ApplicationHighContrastAdjustment:
//Windows.UI.Xaml.ApplicationContrastMode (@020000006)
public enum ApplicationHighContrastAdjustment : uint
{
None = 0u,
Auto = 4294967295u
}
Большинство перечислений 0, 1, 2, ...
, Но у этого есть другие значения, определенные на членах перечисления:
- 0
- 4294967295
Как я могу прочитать получить эти значения UInt32
Примечание: вопрос не обязательно относится только к WinRT. Те же интерфейсы используются в мире C# для проверки управляемых сборок.NET. WinRT использует один и тот же формат файла сборки.
Длинная версия
я использую IMetadataImport
читать содержимое *.winmd
(современная версия TLB для приложений WinRT). Но вопрос в равной степени относится и к чтению метаданных об управляемой сборке.NET.
Сокращенная версия того, как начать чтение файла метаданных 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.UI.Xaml.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.
Получение информации о перечислении (Авто, Нет)
Теперь у нас есть читатель. Вместо того, чтобы перечислять типы в сборке, я могу перейти к интересному вопросу: 0x02000006
:
//Get metadata for enum Windows.UI.Xaml.ApplicationHighContrastAdjustment
mdToken tokenID = 0x02000006; //Windows.UI.Xaml.ApplicationHighContrastAdjustment
//btw, this is all hypothetical code that is vaguely C#/Java-like.
Pointer enum = null;
mdToken memberID;
int nCount;
while (reader.EnumMembers(ref enum, tokenID, out memberID, 1, out nCount) == S_OK)
{
//out MemberID receives the TokenID of each member of the enumeration
}
reader.CloseEnum(enum);
Призыв к EnumMembers
возвращает нам три члена перечисления:
- Windows.UI.Xaml.ApplicationContrastMode (@ 02000006)
- value__ (@ 04000439, private)
- Нет (@0400043A, общедоступный)
- Авто (@0400043B, общедоступный)
Получение информации о каждом значении перечисления
На самом деле мы узнаем их имена (и тот факт, что одно из них закрытое) через вызов GetMemberProps:
IMetaDataImporter.GetMemberProps(0x0400043A, ...); //"None"
IMetaDataImporter.GetMemberProps(0x0400043B, ...); //"Auto"
Примечание. GetMemberProps - это вспомогательная функция. От Microsoft:
Это простой вспомогательный метод: если md является MethodDef, то мы вызываем GetMethodProps; если md является FieldDef, то мы вызываем GetFieldProps. Смотрите эти другие методы для деталей.
Метод GetMemberProps возвращает целый набор информации о каждом значении перечисления, но не их фактическое значение перечисления:
| Metadata | @0400043A | @0400043B |
|-------------------|-------------------|-----------------|
| Name | "None" | "Auto" |
| Attributes | 0x00008056 | 0x00008056 |
| Signature | 06 11 A3 95 | 06 11 A3 95 |
| CodeRVA | 0x00000000 | 0x00000000 |
| CPlusTypeFlag | ELEMENT_TYPE_U4 | ELEMENT_TYPE_U4 |
| DefaultValue | (none) | (none) |
Я не могу найти ничего в свойствах члена, которое указывает назначенное значение перечисления. И, глядя на другие методы IMetadataImporter:
- IMetdataImporter
- GetMemberProps (GetMemberProps - это помощник, который вызывает GetMethodProps или GetFieldProps в зависимости от типа)
- GetMethodProps
- GetFieldProps
- GetPropertyProps
- GetEventProps
- GetParamProps
- GetInterfaceImplProps
- GetCustomAttributeProps
- GetTypeDefProps
- GetTypeRefProps
- GetScopeProps
- GetPermissionSetProps
- GetModuleRefProps
- GetNestedClassProps
- GetMemberRefProps
- GetMemberProps (GetMemberProps - это помощник, который вызывает GetMethodProps или GetFieldProps в зависимости от типа)
Бонус Чтение
- Блоги MSDN: Metadata Unmanaged API (предварительная версия PDF старого документа Word, которая, насколько я могу судить, является единственной документацией Microsoft для API метаданных) ( архив)
1 ответ
Учитывая tokenID члена перечисления, я хочу Значение:
@0400043B = Windows.UI.Xaml.ApplicationHighContrastMode.Auto
Вам нужно перебрать таблицу констант (0x0B
) и найдите, где родительский столбец (columnIndex=1) - это нужный элемент.
Таблица констант выглядит так:
Rid Type (iBYTE) Parent (iCodedToken) Value (iBLOB)
=== =================== ==================== ===============
1 ELEMENT_TYPE_I4 (8) @04000002 00 00 00 00
2 ELEMENT_TYPE_I4 (8) @04000003 01 00 00 00
3 ELEMENT_TYPE_I4 (8) @04000005 00 00 00 00
...
883 ELEMENT_TYPE_I4 (8) @0400040A 02 00 00 00
884 ELEMENT_TYPE_U4 (9) @0400043A 00 00 00 00
885 ELEMENT_TYPE_U4 (9) @0400043B FF FF FF FF
886 ELEMENT_TYPE_I4 (8) @0400043D 00 00 00 00
...
Начиная с IMetadataImporter
вам нужно QueryInterface для этого IMetadataTables
интерфейс:
//Get the tables interface
IMetadataTables tables = reader as IMetadataImporter;
//get the number of rows in the Constant (11) table
UInt32 tabConstant = 11; //the "Constant" table
UInt32 rowSize;
UInt32 rowCount;
UInt32 columnCount;
UInt32 keyColumn;
String tableName;
tables.GetTableInfo(tabConstant,
out rowSize,
out rowCount,
out columnCount,
out keyColumn,
out tableName);
Теперь, когда scunt работает, вам придется вручную итерировать таблицу:
//Loop over ever row in the Constants table
//and look for Parent (columnIndex=1) is the parent we want
//all code released into the public domain; no attribution required
UInt32 desiredToken = 0x0400043B;
UInt32 colParent = 1; // Parent (iCodedToken)
UInt32 colValue = 2; // Value (iBLOB)
for (int i = 0 to rowCount-1)
{
//Get the Parent codedToken of this row
UInt32 value;
tables.GetColumn(tabConstant, colParent, i, outvalue);
// Is it the parent we're interested in (i.e. @0400043A)
if (value != desiredToken)
continue;
// We found it! Get the value from the "Value" (iBLOB) column 2
tables.GetColumn(tabConstant, colValue, i, out value);
//Convert blob UInt32 to a pointer to data
UInt32 dataLen;
Pointer data;
tables.GetBlob(value, out dataLen, out dataLen, out data);
//Convert the dataLen bytes pointed to by data to a UInt32
UInt32 enumValue = PUInt32(data)^;
return enumValue;
}