【问题标题】:Convert class method into IL and execute it at runtime将类方法转换为 IL 并在运行时执行
【发布时间】:2016-12-09 17:15:55
【问题描述】:

我想将一个方法从一个类转换为一个 IL 代码,然后通过调用它来执行它。我下面的例子来自 msdn:https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.createmethodbody(v=vs.110).aspx.

它准确地显示了我的需要,我的问题是从类方法生成 ILcode。

所以基本上我需要填写以下内容

byte[] ILcodes = new byte[] {
      0x02,   /* 02h is the opcode for ldarg.0 */
  0x03,   /* 03h is the opcode for ldarg.1 */
  0x58,   /* 58h is the opcode for add     */
  0x2A    /* 2Ah is the opcode for ret     */
};

来自类中定义的方法,例如:

public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }
}

我尝试了以下调用来填充字节数组:

var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();

以下是我正在创建的示例,但它给出了一个例外: “Common Language Runtime 检测到无效程序。”

 public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }

    // This class will demonstrate how to create a method body using 
    // the MethodBuilder.CreateMethodBody(byte[], int) method.

    public static Type BuildDynType()
    {

        Type addType = null;

        AppDomain currentDom = Thread.GetDomain();

        AssemblyName myAsmName = new AssemblyName();
        myAsmName.Name = "MyDynamicAssembly";

        AssemblyBuilder myAsmBldr = currentDom.DefineDynamicAssembly(
                           myAsmName,
                           AssemblyBuilderAccess.RunAndSave);

        // The dynamic assembly space has been created.  Next, create a module
        // within it.  The type Point will be reflected into this module.

        ModuleBuilder myModuleBldr = myAsmBldr.DefineDynamicModule("MyModule");

        TypeBuilder myTypeBldr = myModuleBldr.DefineType("Adder");

        MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                                MethodAttributes.Public |
                                MethodAttributes.Static,
                                typeof(int),
                                new Type[]
                                {typeof(int), typeof(int)});
        // Build the array of Bytes holding the MSIL instructions.

     //   byte[] ILcodes = new byte[] {
     //         0x02,   /* 02h is the opcode for ldarg.0 */
        //  0x03,   /* 03h is the opcode for ldarg.1 */
        //  0x58,   /* 58h is the opcode for add     */
        //  0x2A    /* 2Ah is the opcode for ret     */
        //};

        var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();

        myMthdBldr.CreateMethodBody(ILcodes, ILcodes.Length);

        addType = myTypeBldr.CreateType();

        return addType;
    }

    public static void TestExecMethod()
    {

        Type myType = BuildDynType();
        Console.WriteLine("---");
        Console.Write("Enter the first integer to add: ");
        int aVal = Convert.ToInt32(Console.ReadLine());

        Console.Write("Enter the second integer to add: ");
        int bVal = Convert.ToInt32(Console.ReadLine());

        object adderInst = Activator.CreateInstance(myType, new object[0]);

        Console.WriteLine("The value of adding {0} to {1} is: {2}.",
                   aVal, bVal,
                        myType.InvokeMember("Add",
                                 BindingFlags.InvokeMethod,
                                 null,
                                 adderInst,
                                 new object[] { aVal, bVal }));
    }

}

通过调用执行:

MethodBodyDemo.TestExecMethod();

有什么帮助吗?

【问题讨论】:

  • 只是出于好奇,你到底想做什么? :) 直接调用方法(或间接使用反射)或使用 System.Reflection.Emit.ILGenerator 创建指令有什么问题吗?
  • 我有点困惑——你基本上是在尝试动态地向一个对象添加一个新方法然后调用它吗?
  • 我正在寻找的是复制一个方法将它发送到另一个系统并且该系统执行它,但另一个系统不知道正在使用的库。上面的代码都是这样做的,唯一的问题是如何填写ILcode?​​span>
  • 有趣的方法,某种验证器/反盗版或只是无聊?如果不是什么秘密,您可以从文本中执行 C# 代码或动态加载程序集/在库上使用 MEF。
  • 你的最终目标是不可能的;字节码包含编译时生成的元数据标记。我上次检查(2010 年)时,它们根据编译器找到它们的顺序递增数字。调用 String.Join 可能会在您的代码中解析为令牌 0A000001,但在您的目标计算机上可能是 0A000009。或者,如果您的目标计算机未在其程序集中调用该方法,它甚至可能不作为元数据令牌存在。您需要将字节数组转换为收件人可以自行编译/解析的格式。有点相关:stackoverflow.com/a/3193423

标签: c# reflection cil methodinfo


【解决方案1】:

我是一个做类似事情的图书馆的作者。它读取现有方法的操作码,将它们重新解释为包含即 MethodInfo 而不是编号标记的通用操作码项,并从中构建方法副本。这适用于交叉组装,也适用于您的情况。

它被设计为猴子补丁游戏,但可以修改为将“明文”中的通用操作码传输到不同的系统,以便目标可以进行类型查找,从而为本地程序集获取正确的操作码。

该项目已获得 MIT 许可,名为 Harmony

【讨论】:

    【解决方案2】:

    Release 模式下构建您的项目,然后执行以下操作之一:

    在此处删除MethodAttributes.Static 属性

    MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                               MethodAttributes.Public |
                               MethodAttributes.Static,
                               typeof(int),
                               new Type[]
                               {typeof(int), typeof(int)});
    

    或者把这个方法改成static

    public int Add(int x, int y)
    {
        return x + y;
    }
    

    这应该适合你。

    但是,我在 cmets 中看到您希望它将字节传递给其他系统并运行它,我的回答只是针对您在问题中的示例。实际上,这是行不通的,因为 Simon Svensson 在评论中写道。 (除非所有方法都是静态的,并且做纯操作(如返回 3+4)而不引用其他方法\类型\字段。)

    你仍然可以(理论上)做一些魔法来让它发挥作用,但实际上并没有。

    【讨论】:

      猜你喜欢
      • 2011-06-17
      • 2017-12-27
      • 2016-05-23
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 2019-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多