[英]Mono.Cecil: Insert a log statement in method's beginning
我正在使用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);
}
}
好吧,這很有趣:)這是我的工作示例(代碼中的注釋,如果不清楚的話,可以隨意提問):
修改后的樣本(實際寫出參數):
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);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.