WinMD是二进制元数据文件,包含有关本地WinRT dll中可用命名空间、类型、类、方法和参数的所有信息。
使用API元数据(.winmd文件)公开Windows Runtime。这与.NET框架(Ecma-335)使用的格式相同。底层的二进制合同使您可以直接访问所选开发语言中的Windows Runtime API。
每个.winmd文件都公开一个或多个命名空间。这些命名空间按提供的功能进行分组。命名空间包含类型,例如类、结构和枚举。
那么,如何访问它呢?
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);
}
在WinRT世界中,我们如何处理*.winmd
文件?
根据Larry Osterman的说法:
从idl文件生成winmd文件。 winmd文件是类型的规范定义。然后将其传递给语言投影。 语言投影读取winmd文件,它们知道如何获取该winmd文件的内容-这是一个二进制文件-并针对该语言生成适当的语言结构。
它们都读取那个winmd文件。 它恰好是一个ECMA-335仅包含元数据的程序集。 这是包装文件格式的技术详细信息。
生成winmd的好处之一是因为它是规则的,所以现在我们可以构建工具来对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框架或.NET运行时中运行。
- WinRT适用于本地代码
- .NET Framework Class Library适用于托管代码
WinRT是本地代码的类库。 .NET人员已经有了自己的类库。
额外问题
本机mscore
中有哪些函数可以处理ECMA-335二进制文件的元数据?