2012-06-17 12 views
8

Estoy usando Mono Cecil para inyectar código en otro método. Quiero agregar un bloque Try-Catch alrededor de mi código.Agregue un try-catch con Mono Cecil

Así que escribí un HelloWorld.exe con un bloque try catch y lo descompilé.

Parece que este en el reflector para el Try-Catch:

.try L_0001 to L_0036 catch [mscorlib]System.Exception handler L_0036 to L_003b 

¿Cómo puedo inyectar un intento de captura como esto a través de Cecil mono?

Respuesta

19

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 [.

+2

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

+0

@Daniel, buena captura, permítanme agregar el permiso faltante. Gracias por el aviso. –

+1

gracias por esta rápida respuesta! trabajando bien - ¡y tan fácil! ¡muchas gracias! – cyptus

Cuestiones relacionadas