2011-12-31 14 views
6

Quiero desmontar todo un ensamblado .NET con ILSpy.ILSpy, cómo resolver dependencias?

que utiliza este código como base de:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx

y funciona bien, sólo cuando tengo un montaje que hace referencia a Npgsql.dll (o cualquier otro conjunto no GAC), entonces me sale un AssemblyResolutionException.

No se ha podido resolver el montaje: 'Npgsql, versión = 2.0.11.92, Culture = neutral, PublicKeyToken = 5d8b90d52f46fda7'

Sé lo que puedo conseguir los ensamblados de referencia, pero ¿cómo puedo añadir a 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 
+0

'//assembly.GetReferencedAssemblies();' en el código original de su enlace fue ' GetReferencedAssemblies (assembly); ', ¿lo omitió porque no definen qué es' GetReferencedAssemblies' en el artículo? Ese código probablemente te ayude. –

+0

@ M.Babcock: cierto, pero este código no está en ninguna parte. Y en ObjectManager, no hay ningún método GetReferencedAssemblies que tome el ensamblaje como parámetro ... –

Respuesta

13

Necesita decirle a Cecil, el lector de metadatos subyacente que ILSpy está utilizando, dónde están sus ensamblajes. Puede escribir:

var resolver = new DefaultAssemblyResolver(); 
resolver.AddSearchDirectory("path/to/my/assemblies"); 

var parameters = new ReaderParameters 
{ 
    AssemblyResolver = resolver, 
}; 

var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters); 

Esta es la manera más natural de decirle a Cecil dónde resolver los ensambles a los que se hace referencia. De esta forma puede eliminar la línea donde carga el ensamblaje usando System.Reflection, y solo usa la pila ILSpy.

+0

Hay otro ángulo para este problema, @JB Evain. Si estoy descompilando una DLL en un sistema donde los ensamblados referidos no están presentes, la descompilación falla (con excepción). Al menos, me gustaría ver el código de descompilación, para lo que se haya resuelto. Estoy publicando una solución adicional, pero la tuya es ideal. – Nayan

1

Sobre la base de la fuente Mono.Cecil, yo supongo que probablemente podría manejar esto utilizando la clase Mono.Cecil.DefaultAssemblyResolver.

En lugar de este código:

Mono.Cecil.AssemblyDefinition assemblyDefinition = 
    Mono.Cecil.AssemblyDefinition.ReadAssembly(pathToAssembly); 

probar esto:

Mono.Cecil.AssemblyDefinition assemblyDefinition = 
    new Mono.Cecil.DefaultAssemblyResolver().Resolve(System.Reflection.AssemblyName.GetAssemblyName(pathToAssembly).ToString()); 

EDITAR

Mientras mi sugerencia original puede o no puede trabajar (yo nunca lo he hecho, así que no hay garantías), es posible que desee consultar el ensamblaje Mono.Addins.CecilReflector.dll del Mono.Addins project para ayudar a mitigar este tipo de problemas. También se basa en Mono.Cecil (igual que ILSpy), por lo que, aunque la premisa general de que Mono.Addins es una biblioteca de extensibilidad que no satisface tus necesidades, puede contener algún tipo de uso del código para tus propósitos o al menos aprender de él.

2

Además de lo que sugirió JB Evain, este código ayudará a evitar la excepción. Todo lo que tiene que hacer es manejar la excepción en resolver.

No es la mejor manera, lo admito. Pero funciona para este escenario: "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; 
    } 
} 

y usarlo como esto:

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); 




De hecho, me gustaría ver una mejora en el decompilador: actualmente ignora el ReaderParameters que el usuario establece, en la clase DefaultAssemblyResolver.

Uso:

var rp = new Mono.Cecil.ReaderParameters(); 
var assemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyStream, rp); 

actual DefaultAssemblyResolver código:

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

Brillante, esto me permitió mostrar el código fuente sin los ensamblados a los que se hace referencia. – TaintedLemon

+1

@Nayan: Gracias, eso es genial. –

+0

@Nayan: Esto se puede lograr mucho más fácil. Mira mi respuesta. Copiar el código fuente de las clases base no es la mejor manera de mejorarlas. –

4

Esto se mejora la respuesta @Nayan. Si desea ignorar asambleas faltante, copiar esta clase:

using Mono.Cecil; 

public class IgnoringExceptionsAssemblyResolver : DefaultAssemblyResolver 
{ 
    public override AssemblyDefinition Resolve(AssemblyNameReference name) 
    { 
     try 
     { 
      return base.Resolve(name); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
} 

y utilizarlo como esa:

var assembly = AssemblyDefinition.ReadAssembly(path, new ReaderParameters() { 
    AssemblyResolver = new IgnoringExceptionsAssemblyResolver() 
}); 
+2

Sí, esto es mucho más limpio. Yo era un novato, cuando escribí esa respuesta :) – Nayan

Cuestiones relacionadas