ILSpy, how to resolve dependencies?

I want to parse the entire .NET assembly using ILSpy.

I used this code as a base:
http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/2488/Default.aspx

And it works fine only when I have an assembly that references Npgsql.dll (or any other non-gac assembly), then I get an AssemblyResolutionException.

Build failed: 'Npgsql, Version = 2.0.11.92, Culture = neutral, PublicKeyToken = 5d8b90d52f46fda7'

I know how I can get reference assemblies, but how can I add them to 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 
+6
source share
4 answers

You have to tell Cecil, the base metadata reader that uses ILSpy, where your assemblies are. You can write:

 var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory("path/to/my/assemblies"); var parameters = new ReaderParameters { AssemblyResolver = resolver, }; var assembly = AssemblyDefinition.ReadAssembly(pathToAssembly, parameters); 

This is the most natural way to tell Cecil where to allow referenced assemblies. This way you can remove the line where you load the assembly using System.Reflection and use only the ILSpy stack.

+13
source

This is an improved answer by @Nayan. If you want to ignore missing assemblies, copy this class:

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

and use it like this:

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

In addition to what JB Evain suggested, this code will help to avoid an exception. All you have to do is handle the exception in the resolver.

Not the best way, I admit. But it works for this scenario: "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; } } 

And use it as follows:

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




I would like to see an improvement in the decompiler: it currently ignores the ReaderParameters that the user sets in the DefaultAssemblyResolver class.

Using:

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

Current DefaultAssemblyResolver code:

 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
source

Based on the source of Mono.Cecil, I would suggest that you would probably Mono.Cecil.DefaultAssemblyResolver this using the Mono.Cecil.DefaultAssemblyResolver class.

Instead of this code:

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

try the following:

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

EDIT

Although my original suggestion may or may not work (I never did, so no guarantees), you can look at the Mono.Addins.CecilReflector.dll assembly from Mono.Addins to help mitigate such problems. It is also based on Mono.Cecil (just like ILSpy), therefore, although the general premise that Mono.Addins is an extensibility library does not meet your needs, it may contain some use of code for your purposes, or at least learn .

+1
source

Source: https://habr.com/ru/post/904814/


All Articles