使用 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 处理程序中。您可以通过 Cecil 轻松完成此操作:
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、离开以优雅地退出 catch 处理程序,最后(双关语),一个 ret。然后,我们只需创建一个 ExceptionHandler 实例来表示一个 try catch 处理程序,该处理程序的 try 包含现有主体,其 catch 是 WriteLine 语句。
需要注意的一件重要事情是范围的结束指令不包含在范围内。它基本上是一个 [TryStart:TryEnd[ 范围。