I'm trying to inject getter property into already compiled assembly.
Without backing field, it's reasonably easy:
AssemblyDefinition targetasmdef = AssemblyFactory.GetAssembly(@"E:\tmp\sandbox\Cecil\ConsoleApplication1\library\bin\Debug\library.dll");
TypeReference returnTypeRef = targetasmdef.MainModule.Import(typeof(string));
MethodDefinition getCity = new MethodDefinition("get_City", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, returnTypeRef);
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Nop));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ldstr, "Warsaw"));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Stloc_0));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ldloc_0));
getCity.Body.CilWorker.Append(getCity.Body.CilWorker.Create(OpCodes.Ret));
getCity.SemanticsAttributes = MethodSemanticsAttributes.Getter;
targetasmdef.MainModule.Types[1].Methods.Add(getCity);
PropertyDefinition City = new PropertyDefinition("City", returnTypeRef, 0) { GetMethod = getCity };
targetasmdef.MainModule.Types[1].Properties.Add(City);
AssemblyFactory.SaveAssembly(targetasmdef, @"E:\tmp\sandbox\Cecil\ConsoleApplication1\library\bin\Debug\library_modified.dll");
Now my question is: Is it possible to create read only property, but with backing field (and set the value of the backing field)?
EDIT:
I've found a solution. Basically, necessary steps are:
- Create backing field, see this post for reference
- Create property, as in above code
Modify constructor body to assign value to property, eg.
var targetasmdef = AssemblyFactory.GetAssembly(@"E:\tmp\library.dll"); var instructions = targetasmdef.MainModule.Types[1].Constructors[0].Body.Instructions; var stringInstructions = (from Instruction Instr in instructions where Instr.OpCode == OpCodes.Ldstr && Instr.Operand != null select new { Instr, Field = instructions[instructions.IndexOf(Instr) + 1] } ).ToList(); foreach (var item in stringInstructions) { Console.WriteLine(string.Format("Load string {0} to field {1}", item.Instr.Operand, item.Field.Operand)); if (item.Field.Operand.ToString().EndsWith("::_name")) item.Instr.Operand = "newName"; } AssemblyFactory.SaveAssembly(targetasmdef, @"E:\tmp\library_modified.dll");