使用 IMetadataImport 时如何获取枚举值
How to get an enumeration value when using IMetadataImport
简短版
如何在使用 IMetadataImport 时从 *.winmd
文件中获取与 enum 关联的数值?
一个很好的例子是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
(WinRT 应用程序的现代版本 TLB)的内容。但这个问题同样适用于读取有关 .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.
获取有关枚举的信息(自动,None)
我们现在有一个 reader。我可以直接跳到这个问题的有趣类型,而不是枚举程序集中的类型: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
returns 我们枚举的三个成员:
- Windows.UI.Xaml.ApplicationContrastMode (@02000006)
- value__ (@04000439, 私有)
- None (@0400043A, public)
- 自动 (@0400043B, public)
获取每个枚举值的信息
我们实际上通过调用 GetMemberProps:
找到了他们的名字(以及一个是私人的)
IMetaDataImporter.GetMemberProps(0x0400043A, ...); //"None"
IMetaDataImporter.GetMemberProps(0x0400043B, ...); //"Auto"
注意:GetMemberProps 是一个辅助函数。来自微软:
This is a simple helper method: if md is a MethodDef, then we call GetMethodProps; if md is a FieldDef, then we call GetFieldProps. See these other methods for details.
GetMemberProps 方法 returns 关于每个枚举值的大量信息——但不是它们的实际枚举 value:
| 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
红利阅读
- MSDN 博客: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)
给定枚举成员的 tokenID 我想要 Value 为:
@0400043B = Windows.UI.Xaml.ApplicationHighContrastMode.Auto
您需要遍历 Constant table (0x0B
),然后找到 Parent 的位置column (columnIndex=1) 是你想要的元素。
常数 table 看起来像:
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 工作,您实际上必须手动迭代 table:
//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;
}
简短版
如何在使用 IMetadataImport 时从 *.winmd
文件中获取与 enum 关联的数值?
一个很好的例子是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
(WinRT 应用程序的现代版本 TLB)的内容。但这个问题同样适用于读取有关 .NET 托管程序集的元数据。
如何起
// 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.
获取有关枚举的信息(自动,None)
我们现在有一个 reader。我可以直接跳到这个问题的有趣类型,而不是枚举程序集中的类型: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
returns 我们枚举的三个成员:
- Windows.UI.Xaml.ApplicationContrastMode (@02000006)
- value__ (@04000439, 私有)
- None (@0400043A, public)
- 自动 (@0400043B, public)
获取每个枚举值的信息
我们实际上通过调用 GetMemberProps:
找到了他们的名字(以及一个是私人的)IMetaDataImporter.GetMemberProps(0x0400043A, ...); //"None"
IMetaDataImporter.GetMemberProps(0x0400043B, ...); //"Auto"
注意:GetMemberProps 是一个辅助函数。来自微软:
This is a simple helper method: if md is a MethodDef, then we call GetMethodProps; if md is a FieldDef, then we call GetFieldProps. See these other methods for details.
GetMemberProps 方法 returns 关于每个枚举值的大量信息——但不是它们的实际枚举 value:
| 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 (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)
给定枚举成员的 tokenID 我想要 Value 为:
@0400043B = Windows.UI.Xaml.ApplicationHighContrastMode.Auto
您需要遍历 Constant table (0x0B
),然后找到 Parent 的位置column (columnIndex=1) 是你想要的元素。
常数 table 看起来像:
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 工作,您实际上必须手动迭代 table:
//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;
}