L'aggiunta di gestori di eccezioni con Mono.Cecil non è difficile, richiede semplicemente di sapere come vengono gestiti i gestori di eccezioni nei metadati.
Diciamo che avete il metodo C#:
static void Throw()
{
throw new Exception ("oups");
}
Se decompilarlo, dovrebbe apparire in qualche modo simile a questo:
.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
}
Ora diciamo che si desidera iniettare codice in questo metodo come è simile al codice C#:
static void Throw()
{
try {
throw new Exception ("oups");
} catch (Exception e) {
Console.WriteLine (e);
}
}
che è, si vuole semplicemente avvolgere il codice esistente in un gestore di catch try. Lo si può fare facilmente con Cecil questo modo:
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);
Questo codice sta manipolando il metodo precedente per assomigliare a questo:
.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
}
Stiamo aggiungendo tre nuove istruzioni: una chiamata a Console.WriteLine , un permesso di uscire con grazia dal gestore delle catture e infine (gioco di parole), un ret. Quindi stiamo semplicemente creando un'istanza ExceptionHandler per rappresentare un gestore catch try il cui tentativo comprende il corpo esistente e il cui catch è l'istruzione WriteLine.
Una cosa importante da notare è che l'istruzione di fine di un intervallo non è contenuta all'interno dell'intervallo. È fondamentalmente un [TryStart: TryEnd [range.
Il flusso di controllo non può cadere da un dispositivo di presa del genere. ECMA-335, §12.4.2.8.1 "L'uscita da blocchi, filtri o gestori protetti non può essere eseguita tramite caduta." (anche se il CLR di Microsoft non sembra applicare questa regola) – Daniel
@Daniel, buona cattura, lasciami aggiungere il congedo mancante. Grazie per il testa a testa. –
grazie per questa risposta veloce! funziona bene - e così facile! Grazie mille! – cyptus