Отказ от ответственности: Я делаю это в целях обучения. Это не будет использоваться в коде.
Я пытаюсь понять, как таблица методов является структурой для обобщений, я хочу динамически добавлять методы во время выполнения. Я нашел очень полезную справку по переполнению стека справочную информацию для начала работы.
У меня есть простой контроллер, который я использую в качестве теста для проверки подмены моих методов:
public class ValuesController : ControllerBase
{
static ValuesController() {
var methodToReplace = typeof(ValuesController).GetMethod(nameof(ValuesController.Seven),
BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var methodToAppend = typeof(ValuesController).GetMethod(nameof(ValuesController.Eight),
BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
new Initializer(methodToReplace, methodToAppend);
}
[HttpGet("Seven")]
public int Seven(string id)
{
return 7;
}
[HttpGet("Eight")]
public int Eight(string id)
{
return 8;
}
}
У меня есть класс Initializer
, который отвечает за обработку добавления к метод.
public class Initializer
{
public Initializer(MethodInfo methodToReplace, MethodInfo methodToAppend)
{
var dummyMethod = typeof(Initializer).GetMethod(nameof(Dummy),
BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var proxyMethod = typeof(Initializer).GetMethod(nameof(Proxy),
BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var appendedMethod = typeof(Initializer).GetMethod(nameof(Appended),
BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
dummyMethod.OneWayReplace(methodToReplace);
methodToReplace.OneWayReplace(proxyMethod);
appendedMethod.OneWayReplace(methodToAppend);
}
public int Proxy(string id)
{
Dummy(id);
return Appended(id);
}
public int Dummy(string id)
{
return 0;
}
public int Appended(string id)
{
return 0;
}
}
А затем у меня есть расширения, которые я получил из исходного вопроса stackoverflow:
public static class InjectionExtensions
{
// Note: This method replaces methodToReplace with methodToInject
// Note: methodToInject will still remain pointing to the same location
public static unsafe MethodReplacementState OneWayReplace(this MethodInfo methodToReplace, MethodInfo methodToInject)
{
//#if DEBUG
RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
//#endif
MethodReplacementState state;
IntPtr tar = methodToReplace.MethodHandle.Value;
var inj = methodToInject.MethodHandle.Value + 8;
if (!methodToReplace.IsVirtual)
tar += 8;
else
{
var index = (int)(((*(long*)tar) >> 32) & 0xFF);
var classStart = *(IntPtr*)(methodToReplace.DeclaringType.TypeHandle.Value + (IntPtr.Size == 4 ? 40 : 64));
tar = classStart + IntPtr.Size * index;
}
#if DEBUG
tar = *(IntPtr*)tar + 1;
inj = *(IntPtr*)inj + 1;
state.Location = tar;
state.OriginalValue = new IntPtr(*(int*)tar);
*(int*)tar = *(int*)inj + (int)(long)inj - (int)(long)tar;
return state;
#else
state.Location = tar;
state.OriginalValue = *(IntPtr*)tar;
* (IntPtr*)tar = *(IntPtr*)inj;
return state;
#endif
}
}
Примечание: Использование текущей настройки все отлично работает Однако после второго изменения класса Initializer
на общий c класс Initializer<T>
возникает нарушение памяти:
System.AccessViolationException: 'Попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена. '
Я предполагаю, что либо вычисление methodToReplace.DeclaringType.TypeHandle.Value
отличается для обобщенных типов, либо поскольку компилятор генерирует класс generi c он записан в защищенную память?
Редактировать Я нашел больше информации, мне нужно правильно подготовить метод при использовании параметров generi c, например:
RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle, new[] { typeof(T).TypeHandle });
Тем не менее, есть еще несколько кусочков головоломки, чтобы это заработало.
Edit
Есть несколько проектов с открытым исходным кодом, таких как harmony которые делают подобные вещи, однако похоже, что они испускают свои собственные сборки. Несмотря на то, что я рассмотрел этот вариант, я все же предпочел бы понять, как я работаю с таблицами методов с обобщениями
Как я могу добавить методы, которые находятся в обобщенных c классах?