我正在使用Mono.Cecil编辑我的目标方法的IL代码,这样我就可以记录该方法的入口点,而无需编辑实际代码。 我能够向一个可以执行日志记录操作的方法插入一个调用指令。 但我不知道如何记录我的目标方法的输入参数。 简而言之,我想在目标方法中插入一条指令,方法是更改它的IL代码来执行日志,或者说打印操作来记录传递给该方法的输入参数值。
我尝试了一个基本程序作为样本。
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace{
// This is my log method, which i want to call in begining of Run() method.
public void LogEntry(string methodName, object[] params)
{
System.Console.WriteLine("******Entered in "+ methodName+" method.***********")
// With params :......
//
}
}
源程序。
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
var processor = runMethod.Body.GetILProcessor();
// get log entry method ref to create instruction
var logEntryMethodReference = targetType.Methods.Single(m => m.Name == "LogEntry");
// Import ..
//
var newInstruction = processor.Create(OpCodes.Call, logEntryMethodReference);
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
// Write the module with default parameters
_module.Write(_targetFileName);
}
}
答案 0 :(得分:3)
嗯,这很有趣:) 这是我的工作样本(代码中的注释,如果不清楚,请随时提出任何问题):
修改样本(实际写出参数):
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace
{
// This is my log method, which i want to call in begining of Run() method.
public static void LogEntry(string methodName, object[] parameters)
{
Console.WriteLine("******Entered in " + methodName + " method.***********");
Console.WriteLine(parameters[0]);
Console.WriteLine(parameters[1]);
}
}
处理IL注射的源程序:
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
// get log entry method ref to create instruction
var logEntryMethodReference = _module.Types.Single(t => t.Name == "Trace").Methods.Single(m => m.Name == "LogEntry");
List<Instruction> newInstructions = new List<Instruction>();
var arrayDef = new VariableDefinition(new ArrayType(_module.TypeSystem.Object)); // create variable to hold the array to be passed to the LogEntry() method
runMethod.Body.Variables.Add(arrayDef); // add variable to the method
var processor = runMethod.Body.GetILProcessor();
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, runMethod.Parameters.Count)); // load to the stack the number of parameters
newInstructions.Add(processor.Create(OpCodes.Newarr, _module.TypeSystem.Object)); // create a new object[] with the number loaded to the stack
newInstructions.Add(processor.Create(OpCodes.Stloc, arrayDef)); // store the array in the local variable
// loop through the parameters of the method to run
for (int i = 0; i < runMethod.Parameters.Count; i++)
{
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array from the local variable
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, i)); // load the index
newInstructions.Add(processor.Create(OpCodes.Ldarg, i+1)); // load the argument of the original method (note that parameter 0 is 'this', that's omitted)
if (runMethod.Parameters[i].ParameterType.IsValueType)
{
newInstructions.Add(processor.Create(OpCodes.Box, runMethod.Parameters[i].ParameterType)); // boxing is needed for value types
}
else
{
newInstructions.Add(processor.Create(OpCodes.Castclass, _module.TypeSystem.Object)); // casting for reference types
}
newInstructions.Add(processor.Create(OpCodes.Stelem_Ref)); // store in the array
}
newInstructions.Add(processor.Create(OpCodes.Ldstr, method)); // load the method name to the stack
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array to the stack
newInstructions.Add(processor.Create(OpCodes.Call, logEntryMethodReference)); // call the LogEntry() method
foreach (var newInstruction in newInstructions.Reverse<Instruction>()) // add the new instructions in referse order
{
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
}
// Write the module with default parameters
_module.Write(_targetFileName);
}
}