如何使用ICSharpCode.Decompiler反编译Moq生成的动态程序集类型
解决Moq动态程序集反编译问题
问题背景
尝试用ICSharpCode.Decompiler反编译Moq生成的动态代理类型时,触发System.NotSupportedException,错误提示动态程序集不支持Location属性。
错误原因
Moq通过System.Reflection.Emit在内存中动态生成代理类型的程序集,这类程序集仅存在于内存中,没有对应的物理磁盘文件,因此Assembly.Location属性调用会抛出异常——该属性仅对已加载到磁盘的程序集有效。
解决方案
先将动态程序集导出到内存流,再基于内存流构建ICSharpCode.Decompiler所需的PEFile和解析器,避免依赖磁盘路径。具体步骤:
- 检查目标程序集是否为动态程序集
- 动态程序集通过
Assembly.Save方法导出到内存流 - 使用内存流初始化
PEFile,而非磁盘路径 - 调整程序集解析器,适配内存中的动态程序集
修改后的完整代码
using System.IO; using System.Reflection; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using Moq; public record TestModel; internal class Program { public static void Main(string[] args) { foreach (var type in new[] { typeof(TestModel), new Mock<TestModel>().Object.GetType() }) { Assembly currentAssembly = type.Assembly; PEFile module; UniversalAssemblyResolver assemblyResolver; if (currentAssembly.IsDynamic) { // 动态程序集导出到内存流 using var ms = new MemoryStream(); currentAssembly.Save(ms); ms.Position = 0; module = new PEFile("DynamicAssembly.dll", ms, PEStreamOptions.PrefetchEntireImage); assemblyResolver = new UniversalAssemblyResolver(module, false, null); } else { // 普通磁盘程序集按原逻辑处理 module = new PEFile(currentAssembly.Location, PEStreamOptions.PrefetchEntireImage); assemblyResolver = new UniversalAssemblyResolver(currentAssembly.Location, false, null); } var decompilerTypeSystem = new DecompilerTypeSystem(module, assemblyResolver); var decompilerSettings = new DecompilerSettings(); var decompiler = new CSharpDecompiler(module, assemblyResolver, decompilerSettings); var typeDefinition = decompilerTypeSystem.MainModule.GetTypeDefinition(new FullTypeName(type.FullName)); var decompiledCode = decompiler.DecompileAsString(typeDefinition.MetadataToken); Console.WriteLine("=== 反编译结果 ==="); Console.WriteLine(decompiledCode); Console.WriteLine("\n"); } } }
项目配置(无需修改)
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="ICSharpCode.Decompiler" Version="8.1.0.7455" /> <PackageReference Include="Moq" Version="4.18.4" /> </ItemGroup> </Project>
说明
- 动态程序集导出时,给
PEFile指定的DynamicAssembly.dll是虚拟文件名,仅用于标识,不会实际写入磁盘 - 调整后的代码同时兼容普通磁盘程序集和Moq生成的动态程序集的反编译需求
内容的提问来源于stack exchange,提问作者Node.JS
相关产品推荐
相关产品推荐

