如何阅读winmd(WinRT的元数据文件)?

问题描述 投票:-1回答:2

一个WinMD是一个二进制文件medadata,包含您需要了解的命名空间,类型,类,方法,在原生的WinRT DLL可用参数的一切。

Windows Runtime design

Windows运行时使用API​​的元数据(.winmd文件)曝光。这是由.NET框架(ECMA-335)中使用的相同的格式。底层的二进制合同,很容易让你直接访问Windows运行时API在您所选择的开发语言。

每个.winmd文件公开一个或多个命名空间。这些命名空间是由它们提供的功能进行分组。命名空间包含的类型,如类,结构和枚举。

大;我该如何访问呢?

Winmd是COM

引擎盖下的WinRT仍然是COM。和Winmd(的Windows元数据)在WinRT中,从COM旧的TLB(类型库)文件的现代版。

| COM                        | WinRT                          |
|----------------------------|--------------------------------|
| CoInitialize               | RoInitialize                   |
| CoCreateInstance(ProgID)¹  | RoActivateInstance(ClassName)  |
| *.tlb                      | *.winmd                        |
| compiled from idl          | compiled from idl              |
| HKCR\Classes\[ProgID]      | HKLM\Software\Microsoft\WindowsRuntime\ActivatableClassId\[ClassName] |
| Code stored in native dll  | Code stored in native dll      |
| DllGetClassObject          | DllGetClassObject              |
| Is native code             | Is native code                 |
| IUnknown                   | IUnknown (and IInspectible)    |
| stdcall calling convention | stdcall calling convention     |
| Everything returns HRESULT | Everything returns HRESULT     |
| LoadTypeLib(*.tlb)         | ???(*.winmd)                   |

从COM TLB读取元数据

给定一个COM TLB文件(例如stdole.tlb),您可以使用各种Windows函数解析TLB获取信息出来。

LoadTypeLib调用让你的ITypeLib接口:

ITypeLib tlb = LoadTypeLib("c:\Windows\system32\stdole2.tlb");

然后,你就可以开始在类型库中的一切迭代

for (int i = 0 to tlb.GetTypeInfoCount-1)
{
   ITypeInfo typeInfo = tlb.GetTypeInfo(i);
   TYPEATTR typeAttr = typeInfo.GetTypeAttr();

   case typeAttr.typeKind of
   TKIND_ENUM: LoadEnum(typeINfo, typeAttr);
   TKIND_DISPATCH,
   TKIND_INTERFACE: LoadInterface(typeInfo, typeAttr);
   TKIND_COCLASS: LoadCoClass(typeInfo, typeAttr);
   else
      //Unknown
   end;
   typeInfo.ReleaseTypeAttr(typeAttr);
}

我们如何做*.winmd文件相同的WinRT的世界?

从拉里·奥斯特曼:

从IDL文件,我们产生winmd文件。一个winmd文件类型的规范定义。而这正是得到传给了语言的预测。语言投影阅读winmd文件,他们知道如何拍摄winmd文件的内容 - 这是一个二进制文件 - 然后预计,并产生该语言相应的语言结构。

他们所有的读winmd文件。这恰好是一个ECMA-335仅元数据组件。这是包装的文件格式的技术细节。

一个关于生产winmds好东西,因为它是有规律,我们现在可以构建工具进行排序,整理,组合,在winmd文件的方法和类型。

从winmd加载元

我已经使用RoGetMetaDataFile加载WinMD尝试。但RoGetMetaDataFile并不意味着让你直接处理winmd文件。它的目的是让你发现关于你已经知道存在一个类型的信息 - 你知道它的名字。

调用,如果你传递一个winmd名RoGetMetadataFile失败:

HSTRING name = CreateWindowsString("C:\Windows\System32\WinMetadata\Windows.Globalization.winmd");
IMetaDataImport2 mdImport;
mdTypeDef mdType;

HRESULT hr = RoGetMetadataFile(name, null, null, out mdImport, out mdType);


0x80073D54
The process has no package identity

这相当于AppModel错误代码:

#define APPMODEL_ERROR_NO_PACKAGE        15700L

但是RoGetMetadataFile确实,如果你传递一个类成功:

RoGetMetadataFile("Windows.Globalization.Calendar", ...);

元数据分配器

有使用MetaDataGetDispenser创建IMetaDataDispenser的建议。

IMetaDataDispenser dispenser;
MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);

大概你可以使用OpenScope方法打开winmd文件:

打开现有的磁盘上的文件和元数据映射到内存中。 该文件必须包含公共语言运行库(CLR)元数据。

当第一个参数(Scope)是“要打开的文件的名称。”

因此,我们尝试:

IUnknown unk;
dispenser.OpenScope(name, ofRead, IID_?????, out unk);

除了我不知道我应该什么接口进行询问;文档不会说。它确实的话:

元数据的内存副本可以使用从“输入”接口中的一个方法进行查询,或添加到使用从“发射”接口的一个方法。

谁把重点放在单词“进口”和“发”的作者是可能是想提供一个线索 - 没有彻底放弃了答案。

奖金颤振

  • 我不知道在winmd的命名空间或类型(这就是我们正在试图找出)
  • 与WinRT的我不是运行的CLR中托管代码;这是本机代码

我们可以利用这个问题的假设的动机是,我们将要创建的是没有一个又一个语言投影(例如ADA,BPL,B,C)。另一个假想动机是允许IDE,以便能够显示winmd文件的元数据的内容。

此外,请记住WinRT的是不以任何方式与.NET。

  • 它不是托管代码。
  • 它不会在组件中存在。
  • 它不是一个.NET运行环境中运行。
  • 但由于.NET已经为您提供了一种方法与COM互操作,以(并考虑到WinRT的是COM)
  • 您可以从您的托管代码调用的WinRT类

许多人认为的WinRT是.NET的另一个名字。 WinRT中不使用,需要或在.NET,C#,一个.NET Framework,或者.NET运行时操作。

  • WinRT的是本机代码
  • 作为.NET Framework类库是托管代码

WinRT的是一个类库为本地代码。 .NET的人已经有了自己的类库。

奖金问题

什么是原生mscore,让您处理一个ECMA-335的二进制文件的元数据功能?

奖金阅读

com windows-runtime midl winmd
2个回答
1
投票

一个问题是有两套IMetadataDispsenser.OpenScope文档:

虽然Windows运行时文件没有提供任何文件:

riid

所期望的元数据接口的IID被返回;呼叫者将利用界面导入(读取)或发射(写入)的元数据。

.NET Framework版本确实提供文档:

riid

[IN]所需的元数据接口的IID被返回;呼叫者将利用界面导入(读取)或发射(写入)的元数据。

RIID的值必须注明“导入”或“发出”接口之一。有效值是:

  • IID_IMetaDataImport
  • IID_IMetaDataImport2
  • IID_IMetaDataAssemblyImport
  • IID_IMetaDataEmit
  • IID_IMetaDataEmit2
  • IID_IMetaDataAssemblyEmit

所以,现在我们可以开始把一切融合在一起。


  1. 创建元数据分配: IMetadataDispsener dispener; MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);
  2. 使用OpenScope指定要读*.winmd文件。我们要求的IMetadataImport接口,因为我们想从winmd导入数据(而不是将其导出到winmd): //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.
  3. 一旦你的元数据导入,你就可以开始列举在元数据文件的所有类型: Pointer enum = null; mdTypeDef typeID; Int32 nRead; while (reader.EnumTypeDefs(enum, out typeID, 1, out nRead) = S_OK) { ProcessToken(reader, typeID); } reader.CloseEnum(enum);
  4. 而现在在winmd每个typeid的,你可以得到各种属性: void ProcessToken(IMetaDataImport reader, mdTypeDef typeID) { //Get three interesting properties of the token: String typeName; //e.g. "Windows.Globalization.NumberFormatting.DecimalFormatter" UInt32 ancestorTypeID; //the token of this type's ancestor (e.g. Object, Interface, System.ValueType, System.Enum) CorTypeAttr flags; //various flags about the type (e.g. public, private, is an interface) GetTypeInfo(reader, typeID, out typeName, out ancestorTypeID, out flags); }

并有取得某一类型的信息时,需要一些挂羊头卖狗肉:

  • 如果类型是在winmd本身定义:使用GetTypeDefProps
  • 如果类型是“基准”,以存在于另一个winmd类型:使用GetTypeRefProps

分辨出来的唯一方法是尝试读取类型属性假设它是使用GetTypeDefProps类型定义和检查返回值:

  • 如果返回S_OK这是一个类型的参考
  • 如果返回S_FALSE它是一种类型定义 获取类型,包括的属性: 的typeName:例如“Windows.Globalization.NumberFormatting.DecimalFormatter” ancestorTypeID:例如0x10000004 国旗:例如0x00004101 void GetTypeInf(IMetaDataImport reader, mdTypeDef typeID, out String typeName, DWORD ancestorTypeID, CorTypeAttr flags) { DWORD nRead; DWORD tdFlags; DWORD baseClassToken; hr = reader.GetTypeDefProps(typeID, null, 0, out nRead, out tdFlags, out baseClassToken); if (hr == S_OK) { //Allocate buffer for name SetLength(typeName, nRead); reader.GetTypeDefProps(typeID, typeName, Length(typeName), out nRead, out flags, out ancestorTypeID); return; } //We couldn't find it a a type **definition**. //Try again as a type **reference** hr = reader.GetTypeRefProps(typeID, null, 0, out nRead, out tdFlags, out baseClassToken); if (hr == S_OK) { //Allocate buffer for name SetLength(typeName, nRead); reader.GetTypeRefProps(typeID, typeName, Length(typeName), out nRead, out flags, out ancestorTypeID); return; } }

还有其他一些有趣的陷阱,如果你试图破译类型。在Windows运行时,一切都要么是根本:

  • 接口
  • 或一类

结构和枚举也是班;但一类特殊的后裔:

  • 接口
  • System.ValueType - >结构 System.Enum - >枚举 类

宝贵的援助来自:

我相信这是唯一的文档是使用微软的API由EMCA-335装配读取元数据的存在。


0
投票

.winmd文件遵循ECMA-335标准,所以任何代码能够读取.NET程序集可以阅读.winmd文件。

有两个选项我用个人是Mono.CecilSystem.Reflection.Metadata。我个人认为Mono.Cecil能更容易的工作。

© www.soinside.com 2019 - 2024. All rights reserved.