Я хочу изменить способ выполнения метода C # при его вызове, чтобы я мог написать что-то вроде этого:
[Distributed]
public DTask<bool> Solve(int n, DEvent<bool> callback)
{
for (int m = 2; m < n - 1; m += 1)
if (m % n == 0)
return false;
return true;
}
Во время выполнения мне нужно иметь возможность анализировать методы с атрибутом Distributed (что я уже могу делать), а затем вставлять код до выполнения тела функции и после ее возврата. Что еще более важно, мне нужно иметь возможность делать это, не изменяя код, в котором вызывается Solve, или в начале функции (во время компиляции; цель - сделать это во время выполнения).
На данный момент я попытался использовать этот фрагмент кода (предположим, что t - это тип, в котором хранится Solve, а m - MethodInfo для Solve) :
private void WrapMethod(Type t, MethodInfo m)
{
// Generate ILasm for delegate.
byte[] il = typeof(Dpm).GetMethod("ReplacedSolve").GetMethodBody().GetILAsByteArray();
// Pin the bytes in the garbage collection.
GCHandle h = GCHandle.Alloc((object)il, GCHandleType.Pinned);
IntPtr addr = h.AddrOfPinnedObject();
int size = il.Length;
// Swap the method.
MethodRental.SwapMethodBody(t, m.MetadataToken, addr, size, MethodRental.JitImmediate);
}
public DTask<bool> ReplacedSolve(int n, DEvent<bool> callback)
{
Console.WriteLine("This was executed instead!");
return true;
}
Однако MethodRental.SwapMethodBody работает только с динамическими модулями; не те, что уже были скомпилированы и сохранены в сборке.
Итак, я ищу способ эффективно использовать SwapMethodBody для метода, который уже хранится в загруженной и выполняющейся сборке. .
Обратите внимание: если мне нужно полностью скопировать метод в динамический модуль, это не проблема, но в этом случае мне нужно найти способ скопировать через IL, а также обновить все вызовы Solve (), чтобы они укажет на новую копию.
Ответы:
Гармония 2 - это библиотека с открытым исходным кодом (лицензия MIT), предназначенная для замены, украшения или изменения существующих методов C # любого типа во время выполнения. Основное внимание уделяется играм и плагинам, написанным на Mono или .NET. Он заботится о нескольких изменениях одного и того же метода - они накапливаются, а не перезаписывают друг друга.
Он создает динамические методы замены для каждого исходного метода и передает им код, который вызывает пользовательские методы в начале и в конце. Он также позволяет вам писать фильтры для обработки исходного кода IL и настраиваемые обработчики исключений, что позволяет более детально управлять исходным методом.
Чтобы завершить процесс, он записывает простой переход на ассемблере в трамплин исходного метода, который указывает на ассемблер, созданный при компиляции динамического метода. Это работает для 32/64-битных версий Windows, macOS и любых Linux, поддерживаемых Mono.
Документацию можно найти здесь .
пример
( Источник )
Исходный код
Патч с аннотациями Harmony
Как вариант, ручное исправление с отражением
источник
Memory.WriteJump
)?48 B8 <QWord>
перемещает непосредственное значение QWORD кrax
, тоFF E0
естьjmp rax
- все ясно , там! Мой оставшийся вопрос касаетсяE9 <DWord>
случая (ближний прыжок): кажется, в этом случае ближний прыжок сохраняется, и модификация относится к цели прыжка; когда Mono вообще генерирует такой код и почему он получает особую обработку?Для .NET 4 и выше
источник
this
внутри,injectionMethod*()
он будет ссылаться наInjection
экземпляр во время компиляции , но наTarget
экземпляр во время выполнения (это верно для всех ссылок на члены экземпляра, которые вы используете внутри введенного метод). (2) По какой-то причине эта#DEBUG
часть работала только при отладке теста, но не при запуске теста, который был скомпилирован отладкой. В итоге я всегда использовал эту#else
деталь. Я не понимаю, почему это работает, но это так.Debugger.IsAttached
вместо#if
препроцессораВы МОЖЕТЕ изменить содержимое метода во время выполнения. Но вы не должны этого делать, и настоятельно рекомендуется сохранить это в тестовых целях.
Вы только посмотрите:
http://www.codeproject.com/Articles/463508/NET-CLR-Injection-Modify-IL-Code-during-Run-time
В принципе, вы можете:
Возьми эти байты.
Если вы просто хотите добавить или добавить какой-то код, просто добавьте коды операций preprend / append, которые вы хотите (однако будьте осторожны, оставляя стек чистым)
Вот несколько советов по "распаковке" существующего IL:
После изменения ваш байтовый массив IL можно повторно ввести через InjectionHelper.UpdateILCodes (метод MethodInfo, byte [] ilCodes) - см. Ссылку, указанную выше
Это «небезопасная» часть ... Работает хорошо, но заключается во взломе внутренних механизмов CLR ...
источник
вы можете заменить его, если метод не виртуальный, не общий, не общего типа, не встроен и находится на платформе x86:
источник
Существует пара фреймворков, которые позволяют динамически изменять любой метод во время выполнения (они используют интерфейс ICLRProfiling, упомянутый пользователем 152949):
Есть также несколько фреймворков, которые издеваются над внутренними компонентами .NET, они, вероятно, более хрупкие и, вероятно, не могут изменять встроенный код, но с другой стороны они полностью автономны и не требуют использования кастомная пусковая установка.
источник
Решение Логмана , но с интерфейсом для обмена телами методов. Также более простой пример.
источник
Основываясь на ответах на этот и еще один вопрос, ive придумала эту упрощенную версию:
источник
Вы можете заменить метод во время выполнения, используя интерфейс ICLRPRofiling .
См. Этот блог для получения более подробной информации.
источник
Я знаю, что это не точный ответ на ваш вопрос, но обычный способ сделать это - использовать подход фабрики / прокси.
Сначала мы объявляем базовый тип.
Затем мы можем объявить производный тип (назовем его прокси).
Производный тип также может быть создан во время выполнения.
Единственная потеря производительности - во время построения производного объекта, первый раз происходит довольно медленно, потому что он будет использовать много отражений и отражений. Во всех остальных случаях это стоимость одновременного поиска в таблице и конструктора. Как уже говорилось, вы можете оптимизировать строительство, используя
источник