12

別の方法でコードを挿入するためにMonoCecilを使用しています。コードの周りにTry-Catchブロックを追加したいと思います。

そこで、try catchブロックを使用してHelloWorld.exeを作成し、逆コンパイルしました。

Try-CatchのReflectorでは次のようになります。

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

モノセシルを介してこのようなトライキャッチを注入するにはどうすればよいですか?

4

1 に答える 1

24

Mono.Cecil を使用して例外ハンドラーを追加することは難しくありません。必要なのは、例外ハンドラーがメタデータでどのように配置されているかを知っていることだけです。

C# メソッドがあるとします。

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

逆コンパイルすると、次のようになります。

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

3 つの新しい命令を追加しています。Console.WriteLine の呼び出し、catch ハンドラーを正常に終了するための leave、そして最後に (しゃれが意図されています) ret です。次に、try が既存の本体を包含し、その catch が WriteLine ステートメントである try catch ハンドラーを表す ExceptionHandler インスタンスを作成するだけです。

注意すべき重要な点の 1 つは、範囲の終了命令が範囲内に含まれていないことです。これは基本的に [TryStart:TryEnd[ の範囲です。

于 2012-06-17T21:27:05.650 に答える