使用ILSpy存储库中的ICSharpCode.Decompiler
库,我能够反编译一个Lambda方法。
像这样
public void Add(Action<Cursor> action)
{
var asm = Assembly.GetCallingAssembly();
var file = asm.Location;
var resolver = new CustomAssemblyResolver(asm);
var decompiler = new CSharpDecompiler(file, resolver, new DecompilerSettings());
var method = MetadataTokenHelpers.TryAsEntityHandle(action.Method.MetadataToken);
var ast = decompiler.Decompile(new List<EntityHandle>() { method.Value });
}
正如我之前提到的,您需要为
CSharpDecompiler
实例提供一个解析器(
IAssemblyResolver
),其任务是在反编译过程中加载引用的程序集。
ICSharpCode.Decompiler
有一个
UniversalAssemblyResolver
(
source),它通过搜索文件系统中的常见文件夹来完成此任务(它接收一个框架版本以了解在哪里搜索)。我不喜欢它,因为它假设了太多并需要太多信息(我并不总是知道框架或我的运行时的其他部分),所以我创建了自己的解析器,遍历程序集图(从调用程序集开始)。
class CustomAssemblyResolver : IAssemblyResolver
{
private Dictionary<string, Assembly> _references;
public CustomAssemblyResolver(Assembly asm)
{
_references = new Dictionary<string, Assembly>();
var stack = new Stack<Assembly>();
stack.Push(asm);
while (stack.Count > 0)
{
var top = stack.Pop();
if (_references.ContainsKey(top.FullName)) continue;
_references[top.FullName] = top;
var refs = top.GetReferencedAssemblies();
if (refs != null && refs.Length > 0)
{
foreach (var r in refs)
{
stack.Push(Assembly.Load(r));
}
}
}
;
}
public PEFile Resolve(IAssemblyReference reference)
{
var asm = _references[reference.FullName];
var file = asm.Location;
return new PEFile(file, new FileStream(file, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
var baseDir = Path.GetDirectoryName(mainModule.FileName);
var moduleFileName = Path.Combine(baseDir, moduleName);
if (!File.Exists(moduleFileName))
{
throw new Exception($"Module {moduleName} could not be found");
}
return new PEFile(moduleFileName, new FileStream(moduleFileName, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
}
}
我不知道是否适用于所有用例,但它对我来说非常有效,至少到目前为止。