Вопрос по c#, mono.cecil – Добавьте попытку с Mono Cecil

10

Я использую Mono Cecil, чтобы ввести код другим методом. Я хочу добавить блок Try-Catch вокруг моего кода.

Поэтому я написал HelloWorld.exe с блоком try catch и декомпилировал его.

В Reflector для Try-Catch это выглядит так:

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

Как я могу ввести попытку поймать, как это через моно Сесил?

Ваш Ответ

1   ответ
21

просто нужно знать, как обработчики исключений размещены в метаданных.

Допустим, у вас есть метод 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 
}

Мы добавляем три новые инструкции: вызов Console.WriteLine, отпуск для изящного выхода из обработчика перехвата и, наконец, (каламбур предназначен), ret. Затем мы просто создаем экземпляр ExceptionHandler для представления обработчика try catch, который try охватывает существующее тело, а catch - оператор WriteLine.

Важно отметить, что инструкция конца диапазона не содержится внутри диапазона. Это в основном диапазон [TryStart: TryEnd [.

@ Даниэль, хороший улов, позвольте мне добавить пропущенный отпуск. Спасибо за внимание.
Поток управления не может выпадать из обработчика уловов таким образом. ECMA-335, «12.4.2.8.1». Выход из защищенных блоков, фильтров или обработчиков не может быть выполнен через провал. & Quot; (хотя Microsoft CLR, похоже, не применяет это правило)
спасибо за этот быстрый ответ! работает нормально - и так просто! Большое спасибо! cyptus
@wliao, очевидно, это всего лишь пример, а не полное решение. Вы должны настроить IL, чтобы он действовал, и это включает замену инструкций ret в защищенных блоках на что-то другое.
Я получаю InvalidProgramException, когда при проверке с помощью PEVerify произошел сбой с помощью «Возврат из блока try», в качестве последней инструкции IL внутри попытки получено ret, я должен изменить его на «оставить LABEL»; чтобы это работало.

Похожие вопросы