With Mono.Cecil
看起來很簡單,我們只需將目標MethodDefinition
的Body
設置爲源MethodDefinition
的Body
即可。對於簡單的方法,這工作正常。但是對於一些使用自定義類型的方法(例如初始化新對象),它不起作用(在編寫程序集時拋出異常)。使用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;
}
}
然後,它會在當時寫的組件裝回失敗。拋出的異常是ArgumentException
以下消息:
「會員‘的System.Uri’在另一個模塊中聲明,並需從國外進口」
事實上,我也遇到過類似的消息但它的方法調用像(string.Concat
)。這就是爲什麼我試圖導入MethodReference
(您可以在我發佈的代碼中的foreach
循環內看到if
)。那真的是那種情況。但是,這種情況是不同的,我不知道如何正確導入使用/引用類型(在這種情況下它是System.Uri
)。據我所知,應該使用Import
的結果,對於MethodReference
,您可以看到結果用於替換每個Instruction
的Operand
。但對於這種情況下的類型引用,我完全不知道如何。
我希望有人在這裏遇到Mono.Cecil
可以幫我解決這個問題。我認爲它應該很簡單,但也許我不明白Mono.Cecil。感謝您的幫助。
那豈不是更簡單用新的方法的調用來代替身體? –
@JeroenMostert這裏的源代碼'Test'方法只是一個簡單的,實際上它可以是任何複雜的代碼(包含幾十行......)。因此,如果我們每次將這些代碼手動轉換爲'Instructions',那就很難了,而且根本就沒有趣味。我想用現有的方法替換另一個程序集中定義的另一個代碼。我真的認爲這對Mono.Cecil來說是可行的。 – Hopeless
不,我的觀點是 - 你想要的方法體已經被正確編譯(包括類型和程序集引用以及整個hoopla)。爲什麼不用'Target.Test'調用替換'Source.Test'方法體呢?而不是嘗試將它移植到新體中。 (如果單獨程序集的存在是一個問題,則首先將它們組合起來。)無論源或目標的複雜程度如何,這都可以工作。 –