Cecil es una librería creada por Jean-Baptiste Evain que permite examinar y modificar los ensamblados .NET de manera sencilla, a su vez estos cambios pueden sobreescribir el original o ser almacenados en un ensamblado nuevo.
Veamos dos ejemplos sencillos que muestran como funciona Cecil:
Convertir una aplicación de consola a una aplicación Windows.- Si compilamos el siguiente código como una aplicación de consola, al ejecutarse mostrará 2 ventanas (una para la consola y otra para el formulario creado):
csharp:// console.cs using System; using System.Windows.Forms; class Program { static void Main(string[] args) { Form frm = new Form(); frm.Height = 100; frm.Width = 100; frm.ShowDialog(); } }
Para hacer la conversión usando Cecil, lo único que se tiene que hacer es:
csharp:using Mono.Cecil; using Mono.Cecil.Cil; class Program { static void Main(string[] args) { AssemblyDefinition assembly = AssemblyFactory.GetAssembly("consola.exe"); assembly.Kind = AssemblyKind.Windows; AssemblyFactory.SaveAssembly(assembly, "windows.exe"); } }
Si ejecutamos
windows.exe
, esta vez sólo se mostrará el formulario creado.Cambiar el espacio de nombres y modificadores de acceso: En este ejemplo se va a cambiar el espacio de nombres de las clases y los modificadores de acceso de un ensamblado denominado
ClassLibrary1.dll
csharp:namespace ClassLibrary1.Utility { public enum HashMethod { MD5, SHA1 } internal class Security { public static string MD5(string password) { throw new System.Exception("The method or operation is not implemented."); } } class Validation { public static bool IsEmail(string email) { throw new System.Exception("The method or operation is not implemented."); } private void IsUrl(string url) { throw new System.Exception("The method or operation is not implemented."); } private void IsIp(string ip) { throw new System.Exception("The method or operation is not implemented."); } } }
Para hacer realizar los cambios en el ensamblado de arriba:
csharp:using Mono.Cecil; using Mono.Cecil.Cil; namespace CecilDemo { class Program { static void Main(string[] args) { string assemblyName = "ClassLibrary1.dll"; /* Cargar el ensamblado */ AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyName); /* Iterar sobre los tipos del módulo principal */ foreach (TypeDefinition type in assembly.MainModule.Types) { /* Cambiar el espacio de nombres y el * nivel de acceso a todas las clases */ if ("<Module>" != type.Name) { type.Namespace = type.Namespace.Replace("ClassLibrary1", "Buayacorp"); type.Attributes |= TypeAttributes.Public; } /* Hacer que todos los métodos de la clase Validation * sean públicos y miembros de clase (static). */ if ("Validation" == type.Name) { foreach (MethodDefinition method in type.Methods) { /* Eliminar el atributo 'private' de cada uno de los métodos */ if ((method.Attributes & MethodAttributes.Private) == MethodAttributes.Private) { method.Attributes ^= MethodAttributes.Private; } method.Attributes |= MethodAttributes.Public | MethodAttributes.Static; } } } /* Almacenar el nuevo ensamblado en CustomClassLibrary1.dll */ AssemblyFactory.SaveAssembly(assembly, "Custom" + assemblyName); } } }
Si observamos el ensamblado modificado con Reflector, veremos que se realizaron los cambios.
El uso de esta herramienta obviamente va a depender de las necesidades y posibles limitaciones de acceso al código fuente que tengamos, puesto que no sería nada productivo usar Cecil para hacer cosas parecidas a los ejemplos que muestro.