ILSpy, как разрешить зависимости?
Я хочу разобрать всю сборку.NET с помощью ILSpy.
Я использовал этот код в качестве базы:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx
И это работает нормально, только когда у меня есть сборка, которая ссылается на Npgsql.dll (или любую другую сборку, отличную от gac), я получаю исключение AssemblyResolutionException.
Не удалось разрешить сборку: 'Npgsql, версия =2.0.11.92, культура = нейтральная, PublicKeyToken=5d8b90d52f46fda7'
Я знаю, как получить ссылки на сборки, но как их добавить в ast?
// SqlWebAdmin.Models.Decompiler.DecompileAssembly("xy.dll");
public static string DecompileAssembly(string pathToAssembly)
{
//Assembly assembly = Assembly.LoadFrom(pathToAssembly);
System.Reflection.Assembly assembly = System.Reflection.Assembly.ReflectionOnlyLoadFrom(pathToAssembly);
//assembly.GetReferencedAssemblies();
//assembly.GetReferencedAssemblies(assembly);
Mono.Cecil.AssemblyDefinition assemblyDefinition =
Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);
ICSharpCode.Decompiler.Ast.AstBuilder astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);
//new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
using (System.IO.StringWriter output = new System.IO.StringWriter())
{
astBuilder.GenerateCode(new ICSharpCode.Decompiler.PlainTextOutput(output));
string result = output.ToString();
return result;
}
return "";
} // End Function DecompileAssembly
4 ответа
Вы должны сообщить Cecil, основному считывателю метаданных, который использует ILSpy, где ваши сборки. Ты можешь написать:
var resolver = new DefaultAssemblyResolver();
resolver.AddSearchDirectory("path/to/my/assemblies");
var parameters = new ReaderParameters
{
AssemblyResolver = resolver,
};
var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters);
Это наиболее естественный способ сообщить Сесилу, где разрешать сборки, на которые есть ссылки. Таким образом, вы можете удалить строку, в которую вы загружаете сборку, используя System.Reflection, и использовать только стек ILSpy.
Это улучшенный ответ @Nayan. Если вы хотите игнорировать отсутствующие сборки, скопируйте этот класс:
using Mono.Cecil;
public class IgnoringExceptionsAssemblyResolver : DefaultAssemblyResolver
{
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
try
{
return base.Resolve(name);
}
catch
{
return null;
}
}
}
и использовать это так:
var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters() {
AssemblyResolver = new IgnoringExceptionsAssemblyResolver()
});
В дополнение к тому, что предложил JB Evain, этот код поможет избежать исключения. Все, что вам нужно сделать, это обработать исключение в резольвере.
Не лучший способ, я признаю. Но это работает для этого сценария: "If I am decompiling a DLL on a system where the referred assemblies are not present, the decompilation fails (with exception.) At least, i would like to see the decompile code, for whatever has been resolved."
using System;
using System.Collections.Generic;
using Mono.Cecil;
public class MyAssemblyResolver : BaseAssemblyResolver
{
private readonly IDictionary<string, AssemblyDefinition> cache;
public MyAssemblyResolver()
{
this.cache = new Dictionary<string, AssemblyDefinition>(StringComparer.Ordinal);
}
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
if (name == null)
throw new ArgumentNullException("name");
AssemblyDefinition assemblyDefinition = null;
if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
return assemblyDefinition;
try //< -------- My addition to the code.
{
assemblyDefinition = base.Resolve(name);
this.cache[name.FullName] = assemblyDefinition;
}
catch { } //< -------- My addition to the code.
return assemblyDefinition;
}
protected void RegisterAssembly(AssemblyDefinition assembly)
{
if (assembly == null)
throw new ArgumentNullException("assembly");
string fullName = assembly.Name.FullName;
if (this.cache.ContainsKey(fullName))
return;
this.cache[fullName] = assembly;
}
}
И используйте это так:
var rp = new Mono.Cecil.ReaderParameters() { AssemblyResolver = new MyAssemblyResolver() };
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
var astBuilder = new ICSharpCode.Decompiler.Ast.AstBuilder(
new ICSharpCode.Decompiler.DecompilerContext(assemblyDefinition.MainModule));
astBuilder.AddAssembly(assemblyDefinition);
Я бы хотел увидеть улучшение в декомпиляторе: в данный момент он игнорирует ReaderParameters
что пользователь устанавливает, в DefaultAssemblyResolver
учебный класс.
Использование:
var rp = new Mono.Cecil.ReaderParameters();
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp);
Текущий DefaultAssemblyResolver
код:
public override AssemblyDefinition Resolve(AssemblyNameReference name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
AssemblyDefinition assemblyDefinition;
if (this.cache.TryGetValue(name.FullName, out assemblyDefinition))
{
return assemblyDefinition;
}
assemblyDefinition = base.Resolve(name); // <---------
// Is the `ReaderParameters` object set by user, used to resolve in `base` class?
this.cache[name.FullName] = assemblyDefinition;
return assemblyDefinition;
}
Исходя из источника Mono.Cecil, я думаю, что вы могли бы справиться с этим, используя Mono.Cecil.DefaultAssemblyResolver
учебный класс.
Вместо этого кода:
Mono.Cecil.AssemblyDefinition assemblyDefinition =
Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly);
попробуй это:
Mono.Cecil.AssemblyDefinition assemblyDefinition =
new Mono.Cecil.DefaultAssemblyResolver().Resolve(System.Reflection.AssemblyName.GetAssemblyName(pathToAssembly).ToString());
РЕДАКТИРОВАТЬ
Хотя мое первоначальное предложение может или не может работать (я никогда не делал, так что никаких гарантий), вы можете посмотреть в Mono.Addins.CecilReflector.dll
сборка из проекта Mono.Addins, чтобы помочь смягчить подобные проблемы. Он также основан на Mono.Cecil (так же, как и ILSpy), поэтому, хотя общая предпосылка о том, что Mono.Addins - это библиотека расширяемости, не отвечает вашим потребностям, она может содержать некоторый код, используемый для ваших целей или, по крайней мере, извлекать уроки.