Преобразуйте метод класса в 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.