我正在使用DynamicMethod并且目标是执行以下操作:
我有一个Action,我使用GetILAsByteArray()
从中获取IL代码作为字节。从这个字节我想创建一个Dynamic方法并执行is。这是我想要做的一个例子:
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" })
时,我们在mscorlib.dll中得到“抛出异常:'System.BadImageFormatException'。”
我不知道的一件事是我应该用作SetCode()
的第二个参数,什么应该用作'maxStackSize'?如何设置与初始操作相同的值?但我认为这不是例外的原因。
如何从IL字节正确创建动态方法?
解决方案
在此,我想总结一下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开发的类,在blog post中描述,但也可以直接下载here。
答案 0 :(得分:3)
你可以这样做:
byte[] code = body.GetILAsByteArray();
ILReader reader = new ILReader(method);
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code);
reader.Accept(visitor);
ilInfo.SetCode(code, body.MaxStackSize);
ILReader
是一个为你努力工作的课程。您可以从here复制它。
示例:
MethodInfo method = ...
DynamicMethod dm = new DynamicMethod(
method.Name,
method.ReturnType,
method.GetParameters.Select(pi => pi.ParameterType).ToArray(),
method.DeclaringType,
skipVisibility: true\fasle - 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
构造函数的paramters数组的第一个单元格中传递方法的instacne类型。
<强>更新强>
由于null
不是静态方法,因此您无法在dm.Invoke(**null**, new object[] { "World" });
传递myAction
。
myAction
(Action<string>
)实际上是一个新生成的类中保存该方法的方法。
但是我检查过,即使我传递myAction.Target
或该类型的新实例,异常也会抛出。例外(CLR dedect一个无效的程序)告诉你IL不完全正确。我现在无法告诉你究竟是什么问题,但如果这对你很重要,我可以在下周回到工作岗位时查看。
无论如何,如果您只想查看DynamicIlInfo.SetCode的运行情况,您可以按原样使用您的代码,但只需更改方法信息:
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
构造函数的paramters数组的第一个单元格中传递方法的instacne类型。
所以你需要这样做:
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" });
现在它的工作非常完美。