从操作创建DynamicMethod的<T>说明


1

我玩弄DynamicMethod的,目标是做到以下几点:

我有我从中获得的IL代码使用GetILAsByteArray()字节的动作。从这个字节我想创建一个动态方法,并执行。这里就是我试图做一个例子:

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字节中正确创建动态方法?


解决方案

在这里,我想总结一下由杜迪·凯莱蒂提供完整的解决方案:

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是类海博罗研制并描述在blog post但也可以直接下载here

  0

我认为你不能使用反射获得maxStackSize值。但事实上,那不是这里的问题。问题在于'Console.WriteLine'调用被编码为元数据标记(可能是MethodRef),而元数据标记仅在声明它的模块范围内有效。看一下'DynamicILInfo.GetTokenFor'函数,这些函数将导入其他元数据项并创建对'DynamicMethod'有效的令牌。 20 10月. 162016-10-20 09:22:28

  0

@thehennyy我试过没有成功,请参阅我的编辑。 20 10月. 162016-10-20 09:36:14

  0

您必须用新创建的'GetTokenFor'方法返回给您的IL字节数组替换旧的标记。 20 10月. 162016-10-20 09:42:02

  0

我以为已经没有意义了,我没有马上得到它。谢谢。有没有一个通用的方法来做到这一点,而不需要知道在行动中被称为什么?例如。有没有办法在最后动态呢? 20 10月. 162016-10-20 09:45:45

  0

是的,您可以解析方法体字节数组,然后使用'Module.Resolvexxx'方法解析所有令牌。 20 10月. 162016-10-20 09:55:57

  0

你为什么要这样做?正如thehennyy所解释的那样,简单的方法由于元数据令牌而不起作用。也许还有其他方法可以实现你想要做的事情。 20 10月. 162016-10-20 14:15:30

  0

这个问题是出于好奇。这可以做的是某种方法的序列化,这听起来像是一件有趣的事情。但我知道在实际应用中这可能不是明智之举。 21 10月. 162016-10-21 07:27:24

1

你可以这样说:

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); 

如果你的方法是一种简单的方法(不通用的,没有例外处理),THID应该工作。

如果你的方法是通用的,你需要传递所有者类型的DynamicMethod的构造做到这一点:

var owner = method.DeclaringType.MakeGenericType(
      method.DeclaringType.GetGenericArguments()); 

还有一两件事,如果仍然没有工作,你的方法是一个实例方法,将该方法的instacne类型传递给构造函数的参数数组的第一个单元格中。

更新

你不可错过null这里dm.Invoke(**null**, new object[] { "World" });因为myAction不是一个静态方法。

myActionAction<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:

显然我昨天很累,我没有意识到你的错误。

正如我在原来的答复中写道,

还有一两件事,如果仍然没有工作,你的方法是一个实例方法,通过该方法的instacne键入paramters的第一个单元格数组的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" }); 

现在,它的工作完美。

  0

“ILInfoGetTokenVisitor”声明在哪里? 26 10月. 162016-10-26 12:39:58

  0

我立即得到'System.Reflection.TargetInvocationException'消息:“{”Bad binary signature。 (来自HRESULT的异常:0x80131192)“}”当试图调用动态方法时。我想我错过了一些基本的东西。 26 10月. 162016-10-26 13:35:35

  0

@ Sjoerd222888查看我的更新示例请 26 10月. 162016-10-26 14:49:14

  0

查看我的更新。我仍然得到一个'TargetInvocationException'。我哪里错了? 27 10月. 162016-10-27 12:05:32

  0

@ Sjoerd222888立即检查 27 10月. 162016-10-27 23:40:20

+1

@ Sjoerd222888现在的工作 28 10月. 162016-10-28 08:39:43

  0

非常感谢您的详细解释。我正要学会使用'DynamicMethod',你的答案是一个很好的帮助! 28 10月. 162016-10-28 09:14:50