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[ の範囲です。