Agregar manejadores de excepciones con Mono.Cecil no es difícil, solo requiere que sepa cómo se establecen los manejadores de excepciones en los metadatos.
Digamos que usted tiene el método C#:
static void Throw()
{
throw new Exception ("oups");
}
Si descompilarlo, debe ser algo similar a esto:
.method private static hidebysig default void Throw() cil managed
{
IL_0000: ldstr "oups"
IL_0005: newobj instance void class [mscorlib]System.Exception::.ctor(string)
IL_000a: throw
}
Ahora vamos a decir que desea inyectar código en este método como es similar al código C#:
static void Throw()
{
try {
throw new Exception ("oups");
} catch (Exception e) {
Console.WriteLine (e);
}
}
Es decir, simplemente desea ajustar el código existente en un manejador de captura de prueba. Puede hacerlo fácilmente con Cecil esta manera:
var method = ...;
var il = method.Body.GetILProcessor();
var write = il.Create (
OpCodes.Call,
module.Import (typeof (Console).GetMethod ("WriteLine", new [] { typeof (object)})));
var ret = il.Create (OpCodes.Ret);
var leave = il.Create (OpCodes.Leave, ret);
il.InsertAfter (
method.Body.Instructions.Last(),
write);
il.InsertAfter (write, leave);
il.InsertAfter (leave, ret);
var handler = new ExceptionHandler (ExceptionHandlerType.Catch) {
TryStart = method.Body.Instructions.First(),
TryEnd = write,
HandlerStart = write,
HandlerEnd = ret,
CatchType = module.Import (typeof (Exception)),
};
method.Body.ExceptionHandlers.Add (handler);
Este código es manipular el método anterior para tener este aspecto:
.method private static hidebysig default void Throw() cil managed
{
.maxstack 1
.try { // 0
IL_0000: ldstr "oups"
IL_0005: newobj instance void class [mscorlib]System.Exception::'.ctor'(string)
IL_000a: throw
} // end .try 0
catch class [mscorlib]System.Exception { // 0
IL_000b: call void class [mscorlib]System.Console::WriteLine(object)
IL_0010: leave IL_0015
} // end handler 0
IL_0015: ret
}
Estamos añadiendo tres nuevas instrucciones: una llamada a Console.WriteLine , una licencia para salir con gracia del controlador de captura, y finalmente (juego de palabras intencionado), un ret. Luego, simplemente estamos creando una instancia de ExceptionHandler para representar un controlador try catch cuyo intento abarca el cuerpo existente y cuya captura es la instrucción WriteLine.
Una cosa importante a tener en cuenta es que la instrucción final de un rango no está contenida dentro del rango. Básicamente es un rango [TryStart: TryEnd [.
No se permite que el flujo de control se caiga de un controlador catch de esa manera. ECMA-335, §12.4.2.8.1 "La salida de bloques, filtros o manipuladores protegidos no se puede realizar a través de un proceso fallido". (aunque Microsoft CLR no parece hacer cumplir esta regla) – Daniel
@Daniel, buena captura, permítanme agregar el permiso faltante. Gracias por el aviso. –
gracias por esta rápida respuesta! trabajando bien - ¡y tan fácil! ¡muchas gracias! – cyptus