With Mono.Cecil
it looks quite simple when we can just set the Body
of the target MethodDefinition
to the Body
of the source MethodDefinition
. For simple methods, that works OK. But for some methods whereas a custom type is used (such as to init a new object), it won't work (with an exception thrown at the time writing the assembly back).
Here is my code:
//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");
The code above was not referenced from anywhere, I just tried it myself and found out it works for simple cases (such as for the 2 methods Test
I posted above). But if the source method (defined in the current app) contains some Type reference (such as some constructor init ...), like this:
public class Form1 {
public string Test(){
var u = new Uri("SomeUri");
return u.AbsolutePath;
}
}
Then it will fail at the time writing the assembly back. The exception thrown is ArgumentException
with the following message:
"Member 'System.Uri' is declared in another module and needs to be imported"
In fact I've encountered a similar message before but it's for method calls like (string.Concat
). And that's why I've tried importing the MethodReference
(you can see the if
inside the foreach
loop in the code I posted). And really that worked for that case. But this case is different, I don't know how to import the used/referenced types (in this case it is System.Uri
) correctly. As I know the result of Import
should be used, for MethodReference
you can see that the result is used to replace the Operand
for each Instruction
. But for Type reference in this case I totally have no idea on how.
I hope someone here experienced with Mono.Cecil
could help me out with this problem. I think it should be simple but maybe I don't understand Mono.Cecil well. Thank you for your help.
Aucun commentaire:
Enregistrer un commentaire