I am playing around with DynamicMethod and aim to do the following:
I have an Action from which I obtain the IL code as bytes using GetILAsByteArray()
. From this bytes I would like to create a Dynamic method and execute is. Here an example of what I am trying to do:
class Program
{
static void Main(string[] args)
{
//Create action and execute
Action<string> myAction = s =>
{
Console.WriteLine("Hello " + s);
};
myAction("World");
//Get IL bytes
byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray();
DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) });
DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo();
dynamicIlInfo.SetCode(ilBytes, 100);
dynamicCallback.Invoke(null, new object[] { "World" });
}
}
When calling a dynamicCallback.Invoke(null, new object[] { "World" })
we get "Exception thrown: 'System.BadImageFormatException' in mscorlib.dll".
One thing I have no idea abut is what I should use as second argument for SetCode()
, what should be used as 'maxStackSize'? How can I set the same value as for the initial action? But I suppose this is not the reason for the exception.
How can I properly create a dynamic method from the IL bytes?
Solution
Here I would like to summarize the complete solution provided by Dudi Keleti:
static void Main(string[] args)
{
Action<string> myAction = s =>
{
Console.WriteLine("Hello " + s);
};
MethodInfo method = myAction.GetMethodInfo();
object target = myAction.Target;
DynamicMethod dm = new DynamicMethod(
method.Name,
method.ReturnType,
new[] {method.DeclaringType}.
Concat(method.GetParameters().
Select(pi => pi.ParameterType)).ToArray(),
method.DeclaringType,
skipVisibility: true);
DynamicILInfo ilInfo = dm.GetDynamicILInfo();
var body = method.GetMethodBody();
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper();
foreach (LocalVariableInfo lvi in body.LocalVariables)
{
sig.AddArgument(lvi.LocalType, lvi.IsPinned);
}
ilInfo.SetLocalSignature(sig.GetSignature());
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
dm.Invoke(target, new object[] { target, "World" });
Console.ReadLine(); //Just to see the result
}
Note: DynamicMethodHelper is class developed by Haibo Luo and described in a blog post but can also be downloaded directly here.
You can do it like this:
ILReader
is a class that do the hard work for you. You can copy it from here.Example:
If your method is a simple method (not generic and without exception handles), thid should work.
If your method is a generic one, you need to do this for passing the owner type to the DynamicMethod constructor:
One more thing, if its still not working, and your method is an instance method, pass the instacne type of the method in the first cell of the paramters array of the
DynamicMethod
constructor.Update
You can't pass
null
heredm.Invoke(**null**, new object[] { "World" });
becausemyAction
is not a static method.myAction
(Action<string>
) is actually a method in a new generated class that hold that method.But I checked and the exception is throwing even if I pass
myAction.Target
or a new instance of that type. The exception (CLR dedect an invalid program) is telling you that the IL is not exactly correct. I can't tell you now exactly what the problem but if it's important to you, I can check it next week when I'll get back to work.Anyway if you just want to see the DynamicIlInfo.SetCode in action you can use your code as is but just change the method info from this:
to this:
Update 2:
Apparently I was very tired yesterday, I did not realize your mistake.
As I wrote in my original answer,
So you need to do this:
And invoke the dynamic method like this:
Now it's work perfect.