在 WinMD 文件中获取命名空间的最佳方法是什么?
What's the best way to get the namespaces in a WinMD file?
我希望以编程方式获取 WinMD 文件中的所有命名空间。我更喜欢 PowerShell 或基于 C# 的解决方案,因为我需要它在脚本中,但只要能完成工作,任何语言都可以。
这是我现在的代码,使用 Assembly.ReflectionOnlyLoadFrom
:
var domain = AppDomain.CurrentDomain;
ResolveEventHandler assemblyHandler = (o, e) => Assembly.ReflectionOnlyLoad(e.Name);
EventHandler<NamespaceResolveEventArgs> namespaceHandler = (o, e) =>
{
string file = WindowsRuntimeMetadata
.ResolveNamespace(e.NamespaceName, Array.Empty<string>())
.FirstOrDefault();
if (file == null)
return;
var assembly = Assembly.ReflectionOnlyLoadFrom(file);
e.ResolvedAssemblies.Add(assembly);
};
try
{
// Load it! (plain .NET assemblies)
return Assembly.LoadFrom(path);
}
catch
{
try
{
// Hook up the handlers
domain.ReflectionOnlyAssemblyResolve += assemblyHandler;
WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve += namespaceHandler;
// Load it again! (WinMD components)
return Assembly.ReflectionOnlyLoadFrom(path);
}
finally
{
// Detach the handlers
domain.ReflectionOnlyAssemblyResolve -= assemblyHandler;
WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve -= namespaceHandler;
}
}
出于某种原因,它似乎不起作用。当我 运行 它时,我在尝试加载 WinMD 文件时收到 ReflectionTypeLoadException
。 (您可以查看 this question 以获得完整的详细信息。)
所以我的问题是,如果 Reflection API 不起作用,最好的方法是什么?当您在 WinRT 类型上按 F12 时,Visual Studio 或 ILSpy 等工具如何执行此操作?有什么方法可以从 PowerShell 执行此操作吗?
TL;DR: 如何从 WinMD 文件中提取所有名称空间?接受任何语言解决方案。
谢谢。
最终接受了@PetSerAl 关于使用 Mono.Cecil 的建议,这实际上非常可靠。这是我最终采用的方法(用 PowerShell 编写):
# Where the real work happens
function Get-Namespaces($assembly)
{
Add-CecilReference
$moduleDefinition = [Mono.Cecil.ModuleDefinition]
$module = $moduleDefinition::ReadModule($assembly)
return $module.Types | ? IsPublic | % Namespace | select -Unique
}
function Extract-Nupkg($nupkg, $out)
{
Add-Type -AssemblyName 'System.IO.Compression.FileSystem' # PowerShell lacks native support for zip
$zipFile = [IO.Compression.ZipFile]
$zipFile::ExtractToDirectory($nupkg, $out)
}
function Add-CecilReference
{
$url = 'https://www.nuget.org/api/v2/package/Mono.Cecil'
$directory = $PSScriptRoot, 'bin', 'Mono.Cecil' -Join '\'
$nupkg = Join-Path $directory 'Mono.Cecil.nupkg'
$assemblyPath = $directory, 'lib', 'net45', 'Mono.Cecil.dll' -Join '\'
if (Test-Path $assemblyPath)
{
# Already downloaded it from a previous script run/function call
Add-Type -Path $assemblyPath
return
}
ri -Recurse -Force $directory 2>&1 | Out-Null
mkdir -f $directory | Out-Null # prevent this from being interpreted as a return value
iwr $url -OutFile $nupkg
Extract-Nupkg $nupkg -Out $directory
Add-Type -Path $assemblyPath
}
您可以找到脚本的全部内容here.
我希望以编程方式获取 WinMD 文件中的所有命名空间。我更喜欢 PowerShell 或基于 C# 的解决方案,因为我需要它在脚本中,但只要能完成工作,任何语言都可以。
这是我现在的代码,使用 Assembly.ReflectionOnlyLoadFrom
:
var domain = AppDomain.CurrentDomain;
ResolveEventHandler assemblyHandler = (o, e) => Assembly.ReflectionOnlyLoad(e.Name);
EventHandler<NamespaceResolveEventArgs> namespaceHandler = (o, e) =>
{
string file = WindowsRuntimeMetadata
.ResolveNamespace(e.NamespaceName, Array.Empty<string>())
.FirstOrDefault();
if (file == null)
return;
var assembly = Assembly.ReflectionOnlyLoadFrom(file);
e.ResolvedAssemblies.Add(assembly);
};
try
{
// Load it! (plain .NET assemblies)
return Assembly.LoadFrom(path);
}
catch
{
try
{
// Hook up the handlers
domain.ReflectionOnlyAssemblyResolve += assemblyHandler;
WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve += namespaceHandler;
// Load it again! (WinMD components)
return Assembly.ReflectionOnlyLoadFrom(path);
}
finally
{
// Detach the handlers
domain.ReflectionOnlyAssemblyResolve -= assemblyHandler;
WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve -= namespaceHandler;
}
}
出于某种原因,它似乎不起作用。当我 运行 它时,我在尝试加载 WinMD 文件时收到 ReflectionTypeLoadException
。 (您可以查看 this question 以获得完整的详细信息。)
所以我的问题是,如果 Reflection API 不起作用,最好的方法是什么?当您在 WinRT 类型上按 F12 时,Visual Studio 或 ILSpy 等工具如何执行此操作?有什么方法可以从 PowerShell 执行此操作吗?
TL;DR: 如何从 WinMD 文件中提取所有名称空间?接受任何语言解决方案。
谢谢。
最终接受了@PetSerAl 关于使用 Mono.Cecil 的建议,这实际上非常可靠。这是我最终采用的方法(用 PowerShell 编写):
# Where the real work happens
function Get-Namespaces($assembly)
{
Add-CecilReference
$moduleDefinition = [Mono.Cecil.ModuleDefinition]
$module = $moduleDefinition::ReadModule($assembly)
return $module.Types | ? IsPublic | % Namespace | select -Unique
}
function Extract-Nupkg($nupkg, $out)
{
Add-Type -AssemblyName 'System.IO.Compression.FileSystem' # PowerShell lacks native support for zip
$zipFile = [IO.Compression.ZipFile]
$zipFile::ExtractToDirectory($nupkg, $out)
}
function Add-CecilReference
{
$url = 'https://www.nuget.org/api/v2/package/Mono.Cecil'
$directory = $PSScriptRoot, 'bin', 'Mono.Cecil' -Join '\'
$nupkg = Join-Path $directory 'Mono.Cecil.nupkg'
$assemblyPath = $directory, 'lib', 'net45', 'Mono.Cecil.dll' -Join '\'
if (Test-Path $assemblyPath)
{
# Already downloaded it from a previous script run/function call
Add-Type -Path $assemblyPath
return
}
ri -Recurse -Force $directory 2>&1 | Out-Null
mkdir -f $directory | Out-Null # prevent this from being interpreted as a return value
iwr $url -OutFile $nupkg
Extract-Nupkg $nupkg -Out $directory
Add-Type -Path $assemblyPath
}
您可以找到脚本的全部内容here.