如何读取winmd(WinRT元数据文件)? [英] How to read a winmd (WinRT metadata file)?

查看:172
本文介绍了如何读取winmd(WinRT元数据文件)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

WinMD是二进制的元数据文件,其中包含您需要了解的有关本机WinRT dll中可用的名称空间,类型,类,方法和参数的所有信息.

A WinMD is a binary medadata file, that contains everything you need to learn about namespaces, types, classes, methods, parameters available in a native WinRT dll.

来自 Windows运行时设计 :

From Windows Runtime design:

使用API​​元数据(.winmd文件)公开Windows运行时.这是.NET框架(Ecma-335)使用的相同格式.底层的二进制协定使您可以轻松地以所选的开发语言直接访问Windows运行时API.

The Windows Runtime is exposed using API metadata (.winmd files). This is the same format used by the .NET framework (Ecma-335). The underlying binary contract makes it easy for you to access the Windows Runtime APIs directly in the development language of your choice.

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

Each .winmd file exposes one or more namespaces. These namespaces are grouped by the functionality that they provide. A namespace contains types such as classes, structures, and enumerations.

太好了;我该如何访问它?

Great; how do I access it?

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

WinRT under the hood is still COM. And Winmd (Windows Metadata) in WinRT, is the modern version of the old TLB (type library) files from COM.

| 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以获取信息.

Reading metadata from a COM tlb

Given a COM tlb file (e.g. stdole.tlb), you can use various Windows functions to parse the tlb to get information out of it.

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

我们如何处理WinRT世界中的*.winmd文件?

How do we do the same with *.winmd files in the WinRT world?

来自拉里·奥斯特曼:

从idl文件中,我们生成一个winmd文件. Winmd文件是该类型的 canonical 定义.这就是语言预测的基础.语言投影读取了winmd文件,并且他们知道如何获取该winmd文件(它是一个二进制文件)的内容,然后进行投影并为该语言生成适当的语言构造.

From the idl files we produce a winmd file. A winmd file is the canonical definition of the type. And that's what get handed off to the language projections. The language projections read the winmd files, and they know how to take the contents of that winmd file - which is a binary file - and then project that and produce the appropriate language constructs for that language.

他们都读取了winmd文件.碰巧是仅ECMA-335元数据的程序集.这就是打包文件格式的技术细节.

They all read that winmd file. It happens to be an ECMA-335 metadata-only assembly. That's the technical detail of the packaging file format.

关于产生winmds的一件好事,因为它是 regular ,我们现在可以构建工具来对winmd文件中的方法和类型进行排序,整理,合并.

One of the nice things about producing winmds, because it's regular, we can now build tooling to sort, collate, combine, the methods and types in a winmd file.

从winmd加载元数据

我尝试使用 加载WinMD.但是 RoGetMetaDataFile 并不意味着可以直接处理winmd文件.它的意思是让您发现有关一种已经知道的类型的信息-并且知道它的名称.

Loading metadata from a winmd

I've tried using RoGetMetaDataFile to load a WinMD. But RoGetMetaDataFile is not meant to let you process a winmd file directly. It is meant to let you discover information about a type that you already know exists - and you know its name.

如果传递winmd文件名,则调用 RoGetMetadataFile 失败:

Calling RoGetMetadataFile fails if you pass it a winmd filename:

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错误代码相对应:

Which corresponds to AppModel error code:

#define APPMODEL_ERROR_NO_PACKAGE        15700L

但是 RoGetMetadataFile 确实可以通过课程:

But RoGetMetadataFile does succeed if you pass a class:

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

MetaData分配器

有人建议使用 IMetaDataDispenser .

MetaData Dispenser

There was a suggestion to use MetaDataGetDispenser to create an IMetaDataDispenser.

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

大概,您可以使用

Presumably you can use the OpenScope method to open a winmd file:

打开一个现有的磁盘文件,并将其元数据映射到内存中.
该文件必须包含公共语言运行时(CLR)元数据.

Opens an existing, on-disk file and maps its metadata into memory.
The file must contain common language runtime (CLR) metadata.

第一个参数(Scope)是要打开的文件的名称."

所以我们尝试:

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

除了我不知道我应该要求什么接口;文档不会说.它确实指出:

Except i don't know what interface i'm supposed to be asking for; the documentation won't say. It does remark:

可以使用导入"界面之一的方法查询元数据的内存中副本,也可以使用发射"界面之一的方法将其添加到内存中.

The in-memory copy of the metadata can be queried using methods from one of the "import" interfaces, or added to using methods from the one of the "emit" interfaces.

着重强调进口" 散发" 的作者可能正试图提供一个线索-不能完全放弃答案.

The author who put the emphasis on the words "import" and "emit" is probably trying to provide a clue - without outright giving away the answer.

  • 我不知道winmd中的名称空间或类型(这就是我们要找出的内容)
  • 使用WinRT时,我不在CLR中运行托管代码;这是针对本机代码的
  • i don't know the namespaces or types in the winmd (that's what we're trying to figure out)
  • with WinRT i'm not running managed code inside a CLR; this is for native code

我们可用于此问题的假设动机是,我们将为尚无语言的语言(例如ada,bpl,b,c)创建一个投影.另一个可能的动机是允许IDE能够显示winmd文件的元数据内容.

The hypothetical motivation we can use for this question is that we're going to be creating a projection for a language that doesn't have one yet (e.g. ada, bpl, b, c). The other hypothetical motivation is to allow an IDE to be able to display metadata contents of a winmd file.

另外,请记住WinRT与.NET毫无关系.

Also, remember that WinRT is not related to .NET in any way.

  • 它不是托管代码.
  • 它在部件中不存在.
  • 它不在.NET运行时内部运行.
  • 但是由于.NET已经为您提供了一种与COM互操作的方法(并且考虑到WinRT是 COM)
  • 您可以从托管代码中调用WinRT类
  • It is not managed code.
  • It does not exist in an assembly.
  • It does not run inside a .NET runtime.
  • But since .NET already provides you a way to interop with COM (and given that WinRT is COM)
  • you are able to call WinRT classes from your managed code

许多人似乎认为WinRT是.NET的别称. WinRT不会使用,要求或在.NET,C#、. NET框架或.NET运行时中运行.

Many people seem to think WinRT is another name for .NET. WinRT does not use, require, or operate in .NET, C#, a .NET framework, or a .NET runtime.

  • WinRT适用于本机代码
  • .NET Framework类库用于托管代码

WinRT是本机代码的类库. .NET人员已经拥有了自己的类库.

WinRT is a class library for native code. .NET people already have their own class library.

本机mscore中的哪些功能可让您处理ECMA-335二进制文件的元数据?

What are the functions in native mscore that lets you process the metadata of an ECMA-335 binary file?

  • Windows Metadata (WinMD) files
  • MIDL3 with @LarryOsterman (video)
  • WinRT internals: WinMD files
  • WinRT and winmd Files
  • Introduction to CLR metadata (archive.is)

推荐答案

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

One problem is there is two sets of documentation for IMetadataDispsenser.OpenScope:

  • IMetaDataDispenser::OpenScope method in the Desktop Windows Runtime documentation
  • IMetaDataDispenser::OpenScope Method in the .NET Framework Unmanaged Reference documentation

尽管Windows运行时文档不提供文档:

And while the Windows Runtime documentation offers no documentation:

riid

要返回的所需元数据接口的IID;调用者将使用该界面导入(读取)或发出(写入)元数据.

The IID of the desired metadata interface to be returned; the caller will use the interface to import (read) or emit (write) metadata.

.NET Framework版本提供提供了文档:

The .NET Framework version does offer documentation:

riid

[in]要返回的所需元数据接口的IID;调用者将使用该界面导入(读取)或发出(写入)元数据.

[in] The IID of the desired metadata interface to be returned; the caller will use the interface to import (read) or emit (write) metadata.

riid的值必须指定"import"或"emit"接口之一.有效值为:

The value of riid must specify one of the "import" or "emit" interfaces. Valid values are:

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

所以现在我们可以开始将所有内容放在一起.

So now we can start to put everything together.

  1. 创建元数据分配器:

  1. Create your metadata dispenser:

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

  • 使用 OpenScope 以指定要读取的*.winmd文件.我们要求提供 IMetadataImport 界面,因为我们想从Winmd中导入数据(而不是导出到Winmd中):

  • Use OpenScope to specify the *.winmd file you want to read. We ask for the IMetadataImport interface, because we want to import data from a winmd (rather than export it to a 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.
    

  • 一旦有了元数据导入器,就可以开始枚举元数据文件中的所有类型:

  • Once you have the metadata importer, you can start to enumerate all the types in the metadata file:

    Pointer enum = null;
    mdTypeDef typeID;
    Int32 nRead;
    while (reader.EnumTypeDefs(enum, out typeID, 1, out nRead) = S_OK)
    {
       ProcessToken(reader, typeID);
    }
    reader.CloseEnum(enum);
    

  • 现在,对于winmd中的每个 typeID ,您可以获取各种属性:

  • And now for each typeID in the winmd you can get various properties:

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

  • 获取有关类型的信息时,需要一些技巧:

    And there's some trickery needed when getting information about a type:

    • 如果类型是在winmd本身中定义的,请使用 GetTypeDefProps
    • 如果该类型是对另一个winmd中存在的类型的引用":请使用 GetTypeRefProps
    • if the type is defined in the winmd itself: use GetTypeDefProps
    • if the type is a "reference" to a type that exists in another winmd: use GetTypeRefProps

    区别的唯一方法是尝试使用 GetTypeDefProps 读取类型属性(假设它是类型定义)并检查返回值:

    The only way to tell the difference is to try to read the type properties assuming it is a type definition using GetTypeDefProps and check the return value:

    • 如果返回S_OK,则为 reference
    • 类型
    • 如果返回S_FALSE,则为定义

    • if it returns S_OK it's a type reference
    • if it returns S_FALSE it's a type definition

    1. 获取该类型的属性,包括:

    1. Get the properties of the type, including:

    • typeName:例如"Windows.Globalization.NumberFormatting.DecimalFormatter"
    • ancestorTypeID:例如0x10000004
    • 标志:例如0x00004101
    • typeName: e.g. "Windows.Globalization.NumberFormatting.DecimalFormatter"
    • ancestorTypeID: e.g. 0x10000004
    • flags: e.g. 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运行时中,所有内容基本上都是:

    There's some other interesting gotchas if you're trying to decipher types. In the Windows Runtime, everything is either fundamentally:

    • 一个界面
    • 或课程

    结构和枚举也是类;但是是特定类别的后代:

    Structs and Enums are also classes; but descendant of a specific class:

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆