WinMD是二进制的元数据文件,其中包含您需要了解的有关本机WinRT dll中可用的名称空间,类型,类,方法和参数的所有信息。
使用API元数据(.winmd文件)公开Windows运行时。这是.NET框架(Ecma-335)使用的相同格式。底层的二进制协定使您可以轻松地以所选的开发语言直接访问Windows运行时API。
每个.winmd文件都公开一个或多个命名空间。这些命名空间按它们提供的功能分组。 名称空间包含诸如类,结构和枚举之类的类型。
太好了;我如何访问它?
引擎盖下的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文件(例如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
文件?
来自拉里·奥斯特曼:
从idl文件中,我们生成一个winmd文件。 Winmd文件是该类型的 canonical 定义。这就是语言预测的基础。语言投影读取了winmd文件,并且他们知道如何获取该winmd文件(它是一个二进制文件)的内容,然后进行投影并为该语言生成适当的语言构造。
他们都读取了winmd文件。碰巧是仅ECMA-335元数据的程序集。这就是打包文件格式的技术细节。
关于生成winmds的一件好事,因为它是 regular ,我们现在可以构建工具来对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
中的名称空间或类型(这就是我们要找出的内容)我们可以用于此问题的假设动机是,我们将为尚无语言的语言(例如ada,bpl,b,c)创建一个投影。另一个可能的动机是允许IDE能够显示winmd文件的元数据内容。
另外,请记住WinRT与.NET毫无关系。
许多人似乎认为WinRT是.NET的别称。 WinRT不会使用,要求或在.NET,C#、. NET框架或.NET运行时中运行。
WinRT是本机代码的类库。 .NET人员已经拥有自己的类库。
本机mscore中的哪些功能可让您处理ECMA-335二进制文件的元数据?
答案 0 :(得分:0)
.winmd文件遵循ECMA-335标准,因此任何能够读取.NET程序集的代码都可以读取.winmd文件。
我个人使用的两个选项是Mono.Cecil和System.Reflection.Metadata。我个人发现Mono.Cecil更易于使用。
答案 1 :(得分:0)
一个问题是 IMetadataDispsenser.OpenScope 有两套文档:
尽管Windows运行时文档不提供任何文档:
riid
要返回的所需元数据接口的IID;调用者将使用该界面导入(读取)或发出(写入)元数据。
.NET Framework版本提供提供了文档:
riid
[in]要返回的所需元数据接口的IID;调用者将使用该界面导入(读取)或发出(写入)元数据。
riid的值必须指定“ import”或“ emit”接口之一。有效值为:
- IID_IMetaDataImport
- IID_IMetaDataImport2
- IID_IMetaDataAssemblyImport
- IID_IMetaDataEmit
- IID_IMetaDataEmit2
- IID_IMetaDataAssemblyEmit
所以现在我们可以开始将所有内容放在一起。
创建元数据分配器:
IMetadataDispsener dispener;
MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IMetaDataDispenser, out dispenser);
使用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.
一旦有了元数据导入器,就可以开始枚举元数据文件中的所有类型:
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 ,您可以获取各种属性:
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);
}
获取有关类型的信息时,需要一些技巧:
区别的唯一方法是尝试使用 GetTypeDefProps 读取类型属性(假设它是类型定义)并检查返回值:
S_OK
,则为 reference 如果返回S_FALSE
,则为定义
获取该类型的属性,包括:
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
->枚举宝贵的援助来自:
我相信是唯一的文档,其中存在有关使用Microsoft API从EMCA-335组件读取元数据的情况。