2011-12-31 34 views
6

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

'//assembly.GetReferencedAssemblies();'あなたのリンクから、元のコードで 'でしたGetReferencedAssemblies(アセンブリ); '、' GetReferencedAssemblies'が記事にあるものを定義していないので、あなたはそれを省略しましたか?そのコードはおそらくあなたを助けるでしょう。 –

+0

@ M.Babcock:真だが、このコードは見つからない。 ObjectManagerには、GetReferencedAssembliesというメソッドがありません。このメソッドはパラメータとしてアセンブリを受け取ります。 –

答えて

13

ILSpyが使用している基礎となるメタデータリーダーであるアセンブリに、Cecilに指示する必要があります。

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

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

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

これは、参照されるアセンブリをどこで解決するかをCecilに伝える最も自然な方法です。この方法で、System.Reflectionを使用してアセンブリをロードする行を削除し、ILSpyスタックのみを使用できます。

+0

この問題のもう一つの角度は@JB Evainです。参照されているアセンブリが存在しないシステムでDLLを逆コンパイルしている場合、逆コンパイルは失敗します(例外もあります)。少なくとも、解決されたものについては、逆コンパイルコードが必要です。私は追加のソリューションを投稿していますが、あなたのものは理想的です。 – Nayan

1

Mono.Cecilソースに基づいて、私はあなたがおそらくMono.Cecil.DefaultAssemblyResolverクラスを使用してこれを扱うことができることを推測します。代わりに、このコードの

:、

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

EDIT

私の元の提案は、または動作しない場合がありますが(私はそれをやったことがない:

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

はこれを試します保証はありません)、Mono.Addins projectからMono.Addins.CecilReflector.dllアセンブリを調べて、これらの問題を軽減することができます問題。 Mono.Addinsが拡張ライブラリであるという一般的な前提がニーズに合わないにもかかわらず、あなたの目的に合ったコードが含まれているか、少なくとも学んでいるかもしれませんが、Mono.Cecilに基づいています(ILSpyと同じです)。

2

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; 
} 
+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

関連する問題