Преобразуйте метод класса в IL и выполните его во время выполнения

Я хочу преобразовать метод в IL-коды из класса, а затем выполнить его, вызвав его. Ниже приведен пример из msdn: https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.createmethodbody(v=vs.110).aspx.

Он показывает именно то, что мне нужно, моя проблема заключается в создании кодов IL из метода класса.

Так что в основном мне нужно заполнить следующее

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

Любая помощь, пожалуйста?

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

И это должно работать для вас.

Но я видел в комментариях, что вы хотите, чтобы он передавал байты в другую систему и запускал ее, мой ответ только для примера, который у вас есть в вопросе. На самом деле это не сработает, потому что причина, которую Саймон Свенссон написал в комментарии. (Если все методы не являются статическими и не выполняют чистых операций (например, возвращают 3+4) без какой-либо ссылки на другие методы \ типы \ поля.)

Вы все еще можете (теоретически) сделать немного магии, чтобы заставить это работать, но это не практически.

Я являюсь автором библиотеки, которая делает подобное. Он считывает коды операций существующего метода, реинтерпретирует их обратно в общие элементы кода операции, которые содержат, например, MethodInfo вместо нумерованного токена, и создает копию метода из них. Это работает кросс-сборки и будет работать в вашем случае тоже.

Он был спроектирован таким образом, чтобы создавать игры-патчи, но его можно было модифицировать для передачи общих кодов операций в "открытом тексте" в другую систему, чтобы получатель мог выполнить поиск типа и, таким образом, получить правильный код операции для локальной сборки.

Проект имеет лицензию MIT и называется Harmony.

Другие вопросы по тегам