gpt4 book ai didi

com - 如何读取 winmd(WinRT 元数据文件)?

转载 作者:行者123 更新时间:2023-12-03 14:33:34 27 4
gpt4 key购买 nike

WinMD 是一个二进制元数据文件,其中包含您需要了解的有关本地 WinRT dll 中可用的命名空间、类型、类、方法和参数的所有内容。
来自 Windows Runtime design :

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.

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.


伟大的;我如何访问它?
Winmd 是 COM
引擎盖下的 WinRT 仍然是 COM。 WinRT 中的 Winmd(Windows 元数据)是来自 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 世界中的文件?
从拉里奥斯特曼:

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.

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.

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 加载元数据
我试过使用 RoGetMetaDataFile 加载 WinMD。但是 RoGetMetaDataFile 并不意味着让您直接处理 winmd 文件。它旨在让您发现有关您已经知道存在的类型的信息 - 并且您知道它的名称。
调用 RoGetMetadataFile 如果您将它传递给 winmd 则失败文件名:
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的方法文件:

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);
除了我不知道我应该要求什么界面;文档不会说。它确实指出:

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.


将重点放在“导入”和“发出”这两个词上的作者可能试图提供一个线索——而不是直接给出答案。
奖金喋喋不休
  • 我不知道 winmd 中的命名空间或类型(这就是我们要弄清楚的)
  • 使用 WinRT,我没有在 CLR 中运行托管代码;这是 native 代码

  • 我们可以用于这个问题的假设动机是,我们将为一种还没有的语言创建一个投影(例如 ada、bpl、b、c)。另一个假设的动机是允许 IDE 能够显示 winmd 文件的元数据内容。
    另外,请记住 WinRT 与 .NET 没有任何关系。
  • 它不是托管代码。
  • 它不存在于程序集中。
  • 它不在 .NET 运行时内运行。
  • 但是由于 .NET 已经为您提供了一种与 COM 互操作的方法(并且鉴于 WinRT 是 COM)
  • 您可以从托管代码中调用 WinRT 类

  • 许多人似乎认为 WinRT 是 .NET 的另一个名称。 WinRT 不使用、不需要或在 .NET、C#、.NET 框架或 .NET 运行时中运行。
  • WinRT 是本地代码
  • 因为 .NET Framework 类库是托管代码

  • WinRT 是 native 代码的类库。 .NET 人已经有了自己的类库。
    奖金问题
    native mscore 中有哪些函数可以让您处理 ECMA-335 二进制文件的元数据?
    奖金阅读
  • Windows Metadata (WinMD) files
  • MIDL3 with @LarryOsterman (视频)
  • WinRT internals: WinMD files
  • WinRT and winmd Files
  • Introduction to CLR metadata archive
  • Avoiding DLL Hell: Introducing Application Metadata in the Microsoft .NET Framework archive
  • 最佳答案

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

  • IMetaDataDispenser::OpenScope method在桌面 Windows 运行时文档中
  • IMetaDataDispenser::OpenScope Method在 .NET Framework 非托管引用文档

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

    riid

    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 版本 提供文件:

    riid

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

    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

    所以现在我们可以开始把所有东西放在一起。
  • 创建您的元数据分配器:
     IMetadataDispsener dispener;
    MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);
  • 使用OpenScope指定 *.winmd您要阅读的文件。我们要求 IMetadataImport 接口(interface),因为我们要 进口 来自 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.
  • 一旦有了元数据导入器,就可以开始枚举元数据文件中的所有类型:
     Pointer enum = null;
    mdTypeDef typeID;
    Int32 nRead;
    while (reader.EnumTypeDefs(enum, out typeID, 1, out nRead) = S_OK)
    {
    ProcessToken(reader, typeID);
    }
    reader.CloseEnum(enum);
  • 现在对于每个 类型 ID 在 winmd 中,您可以获得各种属性:
     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这是一个类型定义
  • 获取类型的属性,包括:
  • 类型名称:例如“Windows.Globalization.NumberFormatting.DecimalFormatter”
  • 祖先类型ID:例如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 运行时中,从根本上来说,一切都是:
  • 一个接口(interface)
  • 或类(class)

  • 结构和枚举也是类;但特定类的后代:
  • 接口(interface)
  • 类(class)
  • System.ValueType --> 结构
  • System.Enum --> 枚举
  • 类(class)


  • 宝贵的帮助来自:
  • Early Stages of the C++ .Net 16 ( archive.is )

  • 我认为唯一的文档是使用 Microsoft 的 API 从 EMCA-335 程序集中读取元数据。

    关于com - 如何读取 winmd(WinRT 元数据文件)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54375771/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com