2017-03-19 3 views
0

Ich verwende Mono.Cecil, um den IL-Code meiner Zielmethode zu bearbeiten, damit ich den Einstiegspunkt dieser Methode protokollieren kann, ohne den eigentlichen Code zu bearbeiten. Ich kann eine Aufrufanweisung in eine Methode einfügen, die Protokollierung durchführen kann. Aber ich weiß nicht, wie ich die Eingabeparameter meiner Zielmethode protokollieren soll. Kurz gesagt, ich möchte eine Instruktion in die Zielmethode einfügen, indem ich ihren IL-Code ändere, um eine Protokoll- oder Sprichwort-Operation auszuführen, um die an diese Methode übergebenen Eingabeparameterwerte zu protokollieren.Mono.Cecil: Einfügen einer Protokollanweisung in Methodenanfang

Ich habe ein Basisprogramm als Beispiel versucht.

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 :...... 
    // 
    } 
} 

Quellprogramm.

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

Das klingt interes Ich denke nicht, dass irgendjemand von uns etwas tun kann, um zu helfen. Bitte posten Sie, was Sie bisher haben (und nicht, um pompös zu sein, aber das gilt normalerweise für jede Frage, die Sie haben werden) jetzt an; einige andere, weniger verständnisvolle Leute könnten Ihnen negative Stimmen geben, selbst wenn Sie den Quellcode für eine Frage wie diese vermissen). –

+0

@Akos Ich habe aktualisiert. – Krishnan

+0

Sind Sie sicher, dass dies in Ordnung ist? Ich denke, hier ist der 'targetType'' Target', aber die 'LogEntry'-Methode befindet sich in der' Trace'-Klasse (beachten Sie, dass sowohl die Klasse als auch die Methode statisch sein müssen). Wenn Sie 'targetType.Methods.Single()' ausführen und nach 'LogEntry' suchen, sucht dieser nach der Methode in 'Target', nicht in' Trace'. Oder vermisse ich etwas? –

Antwort

2

Nun, das war interessant :) Hier ist meine Arbeitsprobe (Kommentare im Code, fühlen Sie sich frei, alles zu fragen, wenn nicht klar):

modifizierte Probe (tatsächlich die Parameter schreiben) :

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

Source-Programm zu behandeln IL Injektion:

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); 
    } 
} 
Verwandte Themen