我正在使用DynamicMethod,目标是做以下事情:
我有一个动作,从中我使用GetILAsByteArray()
获得IL代码作为字节。从这个字节,我想创建一个动态方法和执行是。下面是我要做的一个例子:
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" });
}
}
当调用dynamicCallback.Invoke(null, new object[] { "World" })
时,我们得到"Exception thrown: 'System。BadImageFormatException' in mscorlib.dll".
我不知道的一件事是我应该用什么作为SetCode()
的第二个参数,应该用什么作为'maxStackSize'?如何设置与初始操作相同的值?但我想这并不是例外的原因。
如何正确地从IL字节创建一个动态方法?
<
解决方案/strong>
这里我想总结一下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
}
注意:DynamicMethodHelper是由Haibo Luo开发的类,在一篇博客文章中有描述,但也可以直接在这里下载
你可以这样做:
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
ILReader
是一个为你做艰苦工作的班。你可以从这里复制。
的例子:
MethodInfo method = ...
DynamicMethod dm = new DynamicMethod(
method.Name,
method.ReturnType,
method.GetParameters.Select(pi => pi.ParameterType).ToArray(),
method.DeclaringType,
skipVisibility: truefasle - depends of your need);
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);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
如果你的方法是一个简单的方法(不是泛型的,没有异常句柄),第三步应该可以工作。
如果你的方法是泛型的,你需要这样做来传递所有者类型给DynamicMethod构造函数:
var owner = method.DeclaringType.MakeGenericType(
method.DeclaringType.GetGenericArguments());
还有一件事,如果它仍然不起作用,并且你的方法是一个实例方法,在DynamicMethod
构造函数的参数数组的第一个单元格中传递该方法的实例类型。
更新strong>
你不能在dm.Invoke(**null**, new object[] { "World" });
这里传递null
,因为myAction
不是静态方法。
myAction
(Action<string>
)实际上是一个新生成的类中的方法,该类保存该方法。
但是我检查了,即使我传递myAction.Target
或该类型的新实例,也会抛出异常。异常(CLR检测到无效程序)告诉您IL不完全正确。我现在不能确切地告诉你是什么问题,但如果对你很重要,我可以下周回去工作时检查一下。
不管怎样,如果你只想看DynamicIlInfo。SetCode在操作中,你可以使用你的代码,但只需改变方法信息从this:
class Program
{
static void Main(string[] args)
{
Action<string> myAction = s =>
{
Console.WriteLine("Hello " + s);
};
MethodInfo method = myAction.GetMethodInfo();
//Rest of your code
}
}
:
class Program
{
static void M(string s)
{
Console.WriteLine("Hello " + s);
}
static void Main(string[] args)
{
MethodInfo method = typeof (Program).GetMethod("M", BindingFlags.Static | BindingFlags.NonPublic);
//Rest of your code
}
}
更新2:
显然我昨天很累,我没有意识到你的错误。
正如我在原来的回答中所写的,
还有一件事,如果它仍然不起作用,并且你的方法是一个实例方法,在
DynamicMethod
构造函数的参数数组的第一个单元格中传递该方法的实例类型。
所以你需要这样做:
DynamicMethod dm = new DynamicMethod(
method.Name,
method.ReturnType,
new[] {method.DeclaringType}.
Concat(method.GetParameters().
Select(pi => pi.ParameterType)).ToArray(),
method.DeclaringType,
skipVisibility: true);
并像这样调用动态方法:
dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" });