Hi all,
Mono.Cecil is a great library. It handles a difficult subject most
programers won't know it even exists in their entire life. Mono.Cecil
documentation is... well, there is almost no documentation. So I found
quite hard to come up with some basic stuff that works. These simple
examples might be of interest to other people trying to find their way
around Mono.Cecil. So here is the first one. It follows closely the
sample "Injecting a property" posted on
http://go-mono.com/forums/#nabble-p23098884
by Kasper22 on April 17 2009.
I cut off the custom attributes part as I couldn't test it (and have
no use for it). The load and save symbols part isn't working quite as
it should (the second time I do it, it reports some error) so I
commented it.
This PropertyInject method creates a private field, a public property
and plain get and set methods.
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace CustomFieldsInjection
{
public partial class Injector
{
public static void PropertyInject(string assemblyFilename,
string typeName, string propertyName, Type returnType)
{
//Get the Assembly
AssemblyDefinition assembly = AssemblyFactory.GetAssembly
(assemblyFilename);
//Load the debugger symbols file
// assembly.MainModule.LoadSymbols();
//Get all the types in the given typeName (the type we are
going to add the property to)
TypeDefinition assemblyTypes = assembly.MainModule.Types
[typeName];
//Import the void type
TypeReference voidRef = assembly.MainModule.Import(typeof
(void));
//Import the return type
TypeReference propertyType = assembly.MainModule.Import
(returnType);
//define the field we store the value in
FieldDefinition field = new FieldDefinition(
ConvertToFieldName(propertyName),
propertyType,
Mono.Cecil.FieldAttributes.Private);
assemblyTypes.Fields.Add(field);
//Create the get method
MethodDefinition get = new MethodDefinition("get_" +
propertyName, Mono.Cecil.MethodAttributes.Public |
Mono.Cecil.MethodAttributes.SpecialName |
Mono.Cecil.MethodAttributes.HideBySig, propertyType);
get.Body.CilWorker.Append(get.Body.CilWorker.Create
(OpCodes.Ldarg_0));
get.Body.CilWorker.Append(get.Body.CilWorker.Create
(OpCodes.Ldfld, field));
get.Body.CilWorker.Append(get.Body.CilWorker.Create
(OpCodes.Stloc_0));
Instruction inst = get.Body.CilWorker.Create
(OpCodes.Ldloc_0);
get.Body.CilWorker.Append(get.Body.CilWorker.Create
(OpCodes.Br_S, inst));
get.Body.CilWorker.Append(inst);
get.Body.CilWorker.Append(get.Body.CilWorker.Create
(OpCodes.Ret));
get.Body.Variables.Add(new VariableDefinition("V_0", 0,
get, propertyType));
get.Body.InitLocals = true;
get.SemanticsAttributes =
MethodSemanticsAttributes.Getter;
assemblyTypes.Methods.Add(get);
//Create the set method
MethodDefinition set = new MethodDefinition("set_" +
propertyName, Mono.Cecil.MethodAttributes.Public |
Mono.Cecil.MethodAttributes.SpecialName |
Mono.Cecil.MethodAttributes.HideBySig, voidRef);
CilWorker setWorker = set.Body.CilWorker;
setWorker.Append(setWorker.Create(OpCodes.Ldarg_0));
setWorker.Append(setWorker.Create(OpCodes.Ldarg_1));
setWorker.Append(setWorker.Create(OpCodes.Stfld, field));
setWorker.Append(setWorker.Create(OpCodes.Ret));
set.Parameters.Add(new ParameterDefinition("value", 0,
Mono.Cecil.ParameterAttributes.None, propertyType));
set.SemanticsAttributes =
MethodSemanticsAttributes.Setter;
assemblyTypes.Methods.Add(set);
//create the property
PropertyDefinition propertyDefinition = new
PropertyDefinition(
propertyName,
propertyType, 0) { GetMethod = get, SetMethod = set };
//add the property to the type.
assemblyTypes.Properties.Add(propertyDefinition);
//Save the debugger symbols file
// assembly.MainModule.SaveSymbols();
//Save the patched assembly back to disk
AssemblyFactory.SaveAssembly(assembly, assemblyFilename);
}
private static string ConvertToFieldName(string propertyName)
{
var fieldName = new System.Text.StringBuilder();
fieldName.Append("_");
fieldName.Append(propertyName[0].ToString().ToLower());
if (propertyName.Length > 1)
fieldName.Append(propertyName.Substring(1));
return fieldName.ToString();
}
}
}