2011-12-31 3 views
6

Я хочу разобрать всю сборку .NET с помощью ILSpy.ILSpy, как разрешать зависимости?

Я использовал этот код в качестве основы:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx

И это прекрасно работает, только когда у меня есть узел, который ссылается Npgsql.dll (или любой другой, не GAC сборки), то я получаю AssemblyResolutionException.

Не удалось разрешить сборку: «Npgsql, Version = 2.0.11.92, культура = нейтральной, PublicKeyToken = 5d8b90d52f46fda7»

Я знаю, как я могу получить ссылки сборки, но как я могу добавить их в Аст?

// 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 
+0

'//assembly.GetReferencedAssemblies(); 'в исходном коде от вашей ссылки был' GetReferencedAssemblies (assembly); ', вы его оставили, потому что они не определяют, что такое' GetReferencedAssemblies' в статье? Этот код, вероятно, поможет вам. –

+0

@ M.Babcock: Верно, но этого кода не найти. А в ObjectManager нет метода GetReferencedAssemblies, который принимает сборку как параметр ... –

ответ

13

Вы должны сообщить 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.

+0

Существует еще один угол для этой проблемы, @JB Evain. Если я декомпилирую DLL в системе, где упомянутых сборок нет, декомпиляция завершается с ошибкой (с исключением.) По крайней мере, я хотел бы видеть код декомпиляции для того, что было разрешено. Я отправляю дополнительное решение, но ваш идеал. – Nayan

1

на основе источника 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()); 

EDIT

Хотя мое первоначальное предположение может или не может работать (я никогда не делал этого, поэтому никаких гарантий), вы можете заглянуть в сборку Mono.Addins.CecilReflector.dll из Mono.Addins project, чтобы облегчить проблемы. Он также основан на Mono.Cecil (так же, как ILSpy), поэтому, хотя общая предпосылка, что Mono.Addins является библиотекой расширяемости, не отвечает вашим потребностям, она может содержать некоторое использование кода для ваших целей или, по крайней мере, учиться.

2

В дополнение к предложению JB Evain этот код поможет избежать исключения. Все, что вам нужно сделать, это обработать исключение в resolver.

Не лучший способ, признаюсь. Но это работает для этого сценария: "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; 
} 
+2

Brilliant, это позволило мне отобразить исходный код без ссылочных сборок. – TaintedLemon

+1

@Nayan: Спасибо, это очень здорово. –

+0

@Nayan: Этого можно добиться гораздо проще. Посмотрите на мой ответ. Копирование исходного кода базовых классов не является лучшим способом их улучшения. –

4

Это улучшается @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() 
}); 
+2

Да, это намного чище. Я был новичком, когда я написал этот ответ :) – Nayan