Emisión dinámica de Ensamblados Fecha: 29/Ene/2004 |
. |
En anteriores artículos ( reflexión, del Guille), se expusieron los métodos para Reflexionar un ensamblado. Con este artículo me gustaría ir un poco más allá y usar una de las características más potentes de NameSpace Reflection. Para ello es importante recordar algo sobre reflexión.
La API de reflexión para el .NET Framework está basada en un modelo jerárquico donde cada elemento de un nivel esta compuesto de uno o más elementos de nivel inferior. En la figura 1.1 se ilustra este modelo jerárquico.
Un ensamblado generalmente está compuesto de uno o varios módulos, a su vez un modulo esta compuesto de uno o varios tipo, estos pueden ser clases, delegados y enumeraciones. Cada tipo suele estar compuesto de métodos, propiedades, eventos, campos etc..... Veremos como, basándonos en este esquema obtener dinámicamente un ensamblado, es decir como generar dinámicamente un .exe o un .dll usando Reflection.Emit.
La primera tarea que debemos realizar es crear un AssemblyName:
AssemblyName myAssemblyName = new AssemblyName();
myAssemblyName.Name = "EnsambladoDinamico";Una vez creado el AssemblyName utilizamos la clase AssemblyBuilder para definir un ensamblado dinámico, el constructor de esta clase necesita un dominio de aplicación sobre el que va a correr el ensamblado, como sabréis un dominio de aplicación proporciona aislamiento a los ensamblados así como una definición de limites de seguridad. Para salir del paso por el momento nosotros podemos asignarle el dominio de aplicación actual... de la siguiente manera:
AppDomain myAppDomain = AppDomain.CurrentDomain;
AssemblyBuilder myAssemblyBuilder = myAppDomain.DefineDynamicAssembly(myAssemblyName,AssemblyBuilderAccess.RunAndSave);Ahora seguimos los pasos del esquema y vamos creando sucesivamente un módulo y un tipo para ese módulo:
ModuleBuilder myModuleBuilder = myAssemblyBuilder.DefineDynamicModule("ModuloDinamico","emision.netmodule.mod");
TypeBuilder myTypeBuilder = myModuleBuilder.DefineType("EmitClass");Ahora llega el punto fuerte, crear un método y generar el código IL para el ensamblado. Veremos un ejemplo sencillo de consola dónde se crea un método Main y se llama a WriteLine con un saludito!. Observar que el método se define como público y estático para tomarlo como punto de entrada de la aplicación
MethodBuilder myMethodBuilder = myTypeBuilder.DefineMethod("Main",MethodAttributes.Static|MethodAttributes.Public,CallingConventions.Standard,null,null);
ILGenerator myILgenerator = myMethodBuilder.GetILGenerator();
' Generamos el MSIL
myILgenerator.EmitWriteLine("Hola Mundo!");
myILgenerator.Emit(OpCodes.Ret);
Creamos el tipo y enlazamos el punto de entrada para la aplicación. Ahora ya solo tememos que guardar el ensamblado.
Type myType = myTypeBuilder.CreateType();
myAssemblyBuilder.SetEntryPoint(myMethodBuilder);
myAssemblyBuilder.Save("emision.exe");
SUGERENCIA
El método DefineDynamicAssemby dispone de varias sobrecargas. Dos para módulos de ejecución y otras dos para módulos de persistencia. Si se quiere garantizar la inclusión de un módulo durante la persistencia de un ensamblado, es necesario usar las sobrecargas con el parámetro del nombre de archivo del método DefineDynamicAssemby.