2017-09-05 22 views
0

Mono.Cecilの場合、MethodDefinitionBodyをソースMethodDefinitionBodyに設定するだけでも簡単です。簡単な方法については、うまくいきます。しかし、いくつかのメソッドでは、(新しいオブジェクトを初期化するために)カスタムタイプを使用するのに対し、(アセンブリを書き戻すときに例外がスローされて)動作しません。ここでMono.Cecilを使用してメソッドのBodyを別のメソッドのBodyに置き換えますか?

私のコードです:上記のコードはどこから参照されていなかった

//in current app 
public class Form1 { 
    public string Test(){ 
    return "Modified Test"; 
    } 
} 
//in another assembly 
public class Target { 
    public string Test(){ 
    return "Test"; 
    } 
} 

//the copying code, this works for the above pair of methods 
//the context here is of course in the current app 
var targetAsm = AssemblyDefinition.ReadAssembly("target_path"); 
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test")); 
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target"); 
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test"); 
var m1 = mr1.Resolve(); 
var m1IL = m1.Body.GetILProcessor(); 

foreach(var i in m1.Body.Instructions.ToList()){ 
    var ci = i; 
    if(i.Operand is MethodReference){ 
     var mref = i.Operand as MethodReference; 
     ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref)); 
    } 
    else if(i.Operand is TypeReference){ 
     var tref = i.Operand as TypeReference; 
     ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref)); 
    } 
    if(ci != i){ 
     m1IL.Replace(i, ci); 
    } 
} 
//here the source Body should have its Instructions set imported fine 
//so we just need to set its Body to the target's Body 
m2.Body = m1.Body; 
//finally write to another output assembly 
targetAsm.Write("modified_target_path"); 

、私はちょうどそれを自分自身を試してみましたが、それは(私は上記の投稿、そのような2つの方法Test用など)の単純な例の作品を発見。

public class Form1 { 
    public string Test(){ 
    var u = new Uri("SomeUri"); 
    return u.AbsolutePath; 
    } 
} 

そして、それが戻ってアセンブリを書く時に失敗します。しかし、場合(現在のアプリケーションで定義された)ソースメソッドは次のように、(例えば、いくつかのコンストラクタのinitなど...)いくつかのタイプの参照が含まれています。スローされた例外は、次のようなメッセージでArgumentExceptionです:

「メンバー 『のSystem.Uri』別のモジュールで宣言され、インポートする必要がある」実際に

私は前に同様のメッセージを遭遇しましたそれは(string.Concat)のようなメソッド呼び出しのためです。それで、私がMethodReferenceをインポートしようとしたのです(私が投稿したコードのforeachループ内にifがあります)。そして、本当にそれはその事件のために働いた。しかし、このケースは異なります。使用/参照されるタイプ(この場合はSystem.Uri)を正しくインポートする方法がわかりません。 Importの結果を使用する必要があることを知っているので、MethodReferenceの場合は、それぞれInstructionOperandを置き換えるために結果が使用されています。しかし、この場合の型参照のために、私は完全にどのように考えていません。

ここで誰かがMono.Cecilで経験した人がこの問題を解決してくれることを願っています。私はシンプルであるべきだと思っていますが、私はモノを理解していないかもしれません。ご協力ありがとうございました。

+0

は、新しいメソッドを呼び出して、本体を交換するためにはるかに簡単ではないでしょうか? –

+0

@JeroenMostertここで、ソース 'Test'メソッドは単なる単純なものです。実際には複雑なコード(何十行も含む...)である可能性があります。だから毎回これらのコードを手動で 'Instructions'に変換すれば、それは難しく、全く面白くないでしょう。私は別のアセンブリで定義された別のコードを置き換えるために既存のメソッドを使用したいと思います。私は本当にこれがMono.Cecilと似ていると思います。 – Hopeless

+1

いいえ、私の要点は、あなたが望むメソッド本体が既に正しくコンパイルされていることです(型とアセンブリの参照と全体のホップラを含む)。それを新しいボディに移植しようとするのではなく、 'Source.Test'メソッド本体を' Target.Test'の呼び出しで置き換えてみませんか? (別のアセンブリの存在が問題であれば、最初にILMergeします)。これは、ソースまたはターゲットの複雑さに関係なく機能します。 –

答えて

0

私のすべてのコードは私の質問に掲載されていますが、十分ではありません。実際に例外メッセージ:

VariableDefinitionVariableType文句「メンバー 『可能System.Uri』別のモジュールで宣言され、インポートする必要があります」。私は命令をインポートしますが、変数(正確にはソースMethodBodyから正確に参照されていません)をインポートします。そのため、同じ方法で変数をインポートする必要があります(ExceptionHandlerにはCatchTypeがインポートされるため、ExceptionHandlersもインポートする必要があります)。ここ はVariableDefinitionをインポートするだけで同様のコードです:

var vars = m1.Body.Variables.ToList(); 
m1.Body.Variables.Clear(); 
foreach(var v in vars){ 
    var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType)); 
    m1.Body.Variables.Add(nv); 
} 
関連する問題