2012-06-17 8 views
8

hinzu Ich benutze Mono Cecil, um Code in einer anderen Methode zu injizieren. Ich möchte einen Try-Catch-Block um meinen Code hinzufügen.Fügen Sie einen Versuch-Fang mit Mono Cecil

Also schrieb ich eine HelloWorld.exe mit einem try catch-Block und dekompilierte es.

Es sieht aus wie dies in Reflector für die Try-Catch:

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

Wie kann ich einen Versuch fangen wie dies über Mono cecil injizieren?

Antwort

19

Das Hinzufügen von Ausnahme-Handlern mit Mono.Cecil ist nicht schwierig, es erfordert nur, dass Sie wissen, wie Exception-Handler in den Metadaten angeordnet sind.

Lassen Sie sagen, Sie die C# Methode haben:

static void Throw() 
{ 
    throw new Exception ("oups"); 
} 

Wenn Sie es dekompilieren, sollte es dazu etwas ähnlich aussehen:

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

Nun lassen Sie sagen, dass Sie Code in diese injizieren wollen Verfahren wie es ist ähnlich wie der C# -Code:

static void Throw() 
{ 
    try { 
     throw new Exception ("oups"); 
    } catch (Exception e) { 
     Console.WriteLine (e); 
    } 
} 

das heißt, wollen Sie einfach den vorhandenen Code umwickeln ein Versuch, Catch-Handler. Sie können es mit Cecil tun einfach so:

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); 

Dieser Code die bisherige Methode manipuliert wie folgt aussehen:

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

Wir fügen drei neue Anweisungen: Anruf an Console.WriteLine , ein Verlassen, um den Fanghandhaber zu verlassen, und schließlich (Wortspiel beabsichtigt), ein ret. Dann erstellen wir einfach eine ExceptionHandler-Instanz, die einen try catch-Handler darstellt, dessen try den vorhandenen body umfasst und dessen catch die WriteLine-Anweisung ist.

Eine wichtige Sache zu beachten ist, dass die Endeanweisung eines Bereichs nicht innerhalb des Bereichs enthalten ist. Es ist im Grunde ein [TryStart: TryEnd [Bereich.

+2

Der Steuerungsfluss darf nicht aus einem solchen Catch-Handler herausfallen. ECMA-335, §12.4.2.8.1 "Exit von geschützten Blöcken, Filtern oder Handlern kann nicht durch Durchfallen erreicht werden." (Obwohl die Microsoft CLR diese Regel nicht zu erzwingen scheint) – Daniel

+0

@Daniel, guter Fang, lass mich den fehlenden Urlaub hinzufügen. Danke für die Köpfe hoch. –

+1

danke für diese schnelle antwort! funktioniert gut - und so einfach! vielen Dank! – cyptus

Verwandte Themen