如何在使用IMetadataImport时获取枚举值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在使用IMetadataImport时获取枚举值相关的知识,希望对你有一定的参考价值。

精简版

在使用IMetadataImport时,如何从*.winmd文件中获取与枚举关联的数值?

一个很好的例子是ApplicationHighContrastAdjustment枚举:

//Windows.UI.Xaml.ApplicationContrastMode (@020000006)
public enum ApplicationHighContrastAdjustment : uint
{
    None = 0u,
    Auto = 4294967295u
}

大多数枚举都是0, 1, 2, ...。但是这个在枚举成员上指定了其他值:

  • 0
  • 4294967295

我如何阅读获取那些UInt32值

注意:问题不一定只适用于WinRT。在C#世界中使用相同的接口来检查.NET托管程序集。 WinRT碰巧使用相同的汇编文件格式。

Long version

我正在使用IMetadataImport来读取*.winmd(WinRT应用程序的现代版TLB)的内容。但这个问题同样适用于阅读有关.NET托管程序集的元数据。

关于如何启动和运行阅读winmd元数据文件的abridged version

// Create your metadata dispenser:
IMetadataDispsener dispener;
MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);

//Open the winmd file we want to dump
String filename = "C:WindowsSystem32WinMetadataWindows.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,私有) 无(@ 0400043A,公开) 自动(@ 0400043B,公开)

获取每个枚举值的信息

我们通过调用GetMemberProps实际上找到了他们的名字(以及一个是私有的):

IMetaDataImporter.GetMemberProps(0x0400043A, ...); //"None"
IMetaDataImporter.GetMemberProps(0x0400043B, ...); //"Auto"  

注意:GetMemberProps是一个辅助函数。来自微软:

这是一个简单的辅助方法:如果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

Bonus Reading

  • MSDN博客:Metadata Unmanaged API(旧的Word文档的初步PDF版本,据我所知,是Metadata API唯一的Microsoft文档)(archive
答案

给定枚举成员的tokenID我想要的值为:

@0400043B = Windows.UI.Xaml.ApplicationHighContrastMode.Auto

您需要遍历Constant表(0x0B),并找到Parent列(columnIndex = 1)所需元素的位置。

enter image description here

Constant表看起来像:

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;
}

以上是关于如何在使用IMetadataImport时获取枚举值的主要内容,如果未能解决你的问题,请参考以下文章

获取值类型标签的父枚举

如何在枚举中获取枚举值的 int [重复]

如何获取一个枚举类型元素的个数

如何在 Javascript 中获取 C# 枚举

自定义枚举以及如何在反射中获取枚举值

如何在graphql中获取所有枚举值