2012-06-17 4 views
8

Mono Cecil을 사용하여 다른 방법으로 코드를 주입하고 있습니다. 내 코드 주위에 Try-Catch 블록을 추가하고 싶습니다.Mono Cecil과 함께 try-catch 추가

그래서 try catch 블록을 사용하여 HelloWorld.exe를 작성하고 디 컴파일했습니다.

그것은 시도 - 캐치를위한 반사경에 다음과 같습니다

어떻게 내가 모노 세실를 통해이 같은 시도 캐치를 삽입 할 수

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

답변

19

Mono.Cecil과 함께 예외 처리기를 추가하는 것은 어렵지 않으며 예외 처리기가 메타 데이터에 배치되는 방법을 알아야합니다. 당신이 그것을 컴파일하면

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

, 그것은이 다소 유사합니다 :

하자 당신이 C#을 방법이 있다고

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

이제이 코드를 삽입 할 것을 가정 해 봅시다 메서드는 C# 코드와 비슷합니다.

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

즉, 기존 코드를 try catch 핸들러.

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

이 코드는 다음과 같이하는 이전 방법을 조작한다 : 우리는 세 개의 새로운 명령어를 추가하는

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

: 당신은 세실 쉽게 이런 식으로 그것을 할 수 Console.WriteLine을 호출 , 캐치 핸들러를 정상적으로 나가기위한 휴가, 마침내 (말장난 의도), ret. 그런 다음 ExceptionHandler 인스턴스를 작성하여 try가 기존 본문을 포함하고 catch가 WriteLine 문인 try catch 핸들러를 나타냅니다.

한 가지 중요한 점은 범위의 끝 명령이 범위 내에 포함되지 않는다는 것입니다. 그것은 기본적으로 [TryStart : TryEnd [범위.

+2

제어 흐름이 이와 같은 catch 처리기에서 벗어날 수 없습니다. ECMA-335, §12.4.2.8.1 "보호 된 블록, 필터 또는 처리기를 종료 할 때 가을을 통해 수행 할 수 없습니다." (Microsoft CLR은이 규칙을 적용하지 않는 것 같지만) – Daniel

+0

@Daniel, 좋은 소식, 누락 된 휴가를 추가하겠습니다. 머리를 가져 주셔서 감사합니다. –

+1

이 빠른 답변에 감사드립니다! 잘 작동 - 그리고 너무 쉽게! 대단히 감사합니다! – cyptus