MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Найти вызов метода внутри другого метода"
Answer 866451
Можно взять за основу код библиотеки для парсинга IL-кода SDILReader.
IL-код представляет собой набор инструкций, состоящих и 1 или 2-байтного кода и операнда из 0-8 байт. Вызов метода осуществляется инструкцией call или callvirt, операндом для нее является metadata token, который идентифицирует метод в контексте определенного модуля. Таким образом, найти все методы, которые вызывает указанный метод, можно так:
using System.Reflection; using System.Reflection.Emit; … public static OpCode FindOpCode(short val) { OpCode ret = OpCodes.Nop; FieldInfo[] mas = typeof(OpCodes).GetFields(); for (int i = 0; i < mas.Length; i++) { if (mas[i].FieldType == typeof(OpCode)) { OpCode opcode = (OpCode)mas[i].GetValue(null); if (opcode.Value == val) { ret = opcode; break; } } } return ret; } public static List<MethodBase> GetCalledMethods(MethodInfo mi) { var mb = mi.GetMethodBody(); var msil = mb.GetILAsByteArray(); var module = MethodInfo.GetCurrentMethod().Module; List<MethodBase> methods = new List<MethodBase>(); short op; int n = 0; while (true) { if (n >= msil.Length) break; //получаем код операции if (msil[n] == 0xfe) op = (short)(msil[n + 1] | 0xfe00); else op = (short)(msil[n]); //найдем имя операции OpCode opcode = FindOpCode(op); string str = opcode.Name; int size = 0; //найдем размер операции switch (opcode.OperandType) { case OperandType.InlineBrTarget: size = 4; break; case OperandType.InlineField: size = 4; break; case OperandType.InlineMethod: size = 4; break; case OperandType.InlineSig: size = 4; break; case OperandType.InlineTok: size = 4; break; case OperandType.InlineType: size = 4; break; case OperandType.InlineI: size = 4; break; case OperandType.InlineI8: size = 8; break; case OperandType.InlineNone: size = 0; break; case OperandType.InlineR: size = 8; break; case OperandType.InlineString: size = 4; break; case OperandType.InlineSwitch: size = 4; break; case OperandType.InlineVar: size = 2; break; case OperandType.ShortInlineBrTarget: size = 1; break; case OperandType.ShortInlineI: size = 1; break; case OperandType.ShortInlineR: size = 4; break; case OperandType.ShortInlineVar: size = 1; break; default: throw new Exception("Unknown operand type."); } size += opcode.Size; int token = 0; if (str == "call" || str == "callvirt") { //если это вызов метода, найдем токен token = (((msil[n + 1] | (msil[n + 2] << 8)) | (msil[n + 3] << 0x10)) | (msil[n + 4] << 0x18)); //надем метод в текущем модуле по токену var method = module.ResolveMethod(token); if(!methods.Contains(method))methods.Add(method); } n += size; //пропускаем нужное число байтов } return methods; }
Пример использования:
public static int F(int x) { return x*2; } public static void Test() { int x = System.Diagnostics.Process.GetCurrentProcess().Id; MessageBox.Show(x.ToString()); } private void button1_Click(object sender, EventArgs e) { StringBuilder sb = new StringBuilder(); int y = 0; Func<int,int> lambda = (x) => F(x) > F(y) ? F(y) : F(x / 2); var methods = GetCalledMethods(lambda.Method); sb.AppendLine("Lambda calls:"); foreach (var x in methods) sb.AppendFormat("- {0}.{1}()\r\n", x.DeclaringType.ToString(), x.Name); sb.AppendLine(); methods = GetCalledMethods(this.GetType().GetMethod("Test")); sb.AppendLine("Test method calls:"); foreach (var x in methods) sb.AppendFormat("- {0}.{1}()\r\n", x.DeclaringType.ToString(), x.Name); textBox1.Text = sb.ToString(); }
Должно вывести что-то такое:
Lambda calls:
- WindowsFormsTest.Form1.F()Test method calls:
- System.Diagnostics.Process.GetCurrentProcess()
- System.Diagnostics.Process.get_Id()
- System.Int32.ToString()
- System.Windows.Forms.MessageBox.Show()Что касается изменения кода методов во время выполнения, это значительно сложнее, но тоже возможно. См. например здесь: .NET CLR Injection: Modify IL Code during Run-time
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.