交换属性GetMethod实现运行时

时间:2018-01-08 23:47:50

标签: c# pointers

我目前正在尝试通过将其替换为IL来替换属性获取实现。 我使用这个问题作为参考:How to replace a pointer to a pointer to a method in a class of my method inherited from the system class?

我唯一的区别是我的方法是通过MethodBuilder声明的:

MethodBuilder propertyGetBuilder = builder.DefineMethod
(
    dynamicFunctionName,
    MethodAttributes.Public,
    propertyInfo.PropertyType,
    Type.EmptyTypes
);

ILGenerator propertyGetIlGenerator = propertyGetBuilder.GetILGenerator();

propertyGetIlGenerator.Emit(OpCodes.Ldarg_0);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, propertyInfo.Name);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, relationKeyField.Name);
propertyGetIlGenerator.Emit(OpCodes.Ldstr, relationAttribute.RelationColumn);
propertyGetIlGenerator.Emit(OpCodes.Call, loadRelationMethod);

propertyGetIlGenerator.Emit(OpCodes.Ret);

这为名为BeforeGet{PropertyName}

的生成类型添加了一个新函数

生成新类型后,我将其实例化以确保内存地址存在: dynamic fakeType = Activator.CreateInstance(type);

我从现有类中检索propertyInfo GetMethod,并从新创建的BeforeGet{PropertyName} fakeType类中检索。

之后,在此函数中使用了两个MethodInfo:

RuntimeHelpers.PrepareMethod(methodA.MethodHandle);
RuntimeHelpers.PrepareMethod(methodB.MethodHandle);

unsafe
{
    if (IntPtr.Size == 4)
    {
        int* inj = (int*)methodA.MethodHandle.Value.ToPointer() + 2;
        int* tar = (int*)methodB.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
        Console.WriteLine("\nVersion x86 Debug?\n");

        byte* injInst = (byte*)*inj;
        byte* tarInst = (byte*)*tar;

        int* injSrc = (int*)(injInst + 1);
        int* tarSrc = (int*)(tarInst + 1);

        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
        *tar = *inj;
#endif
    }
    else
    {
        long* inj = (long*)methodA.MethodHandle.Value.ToPointer() + 1;
        long* tar = (long*)methodB.MethodHandle.Value.ToPointer() + 1;
#if DEBUG
        Console.WriteLine("\nVersion x64 Debug\n");
        byte* injInst = (byte*)*inj;
        byte* tarInst = (byte*)*tar;

        int* injSrc = (int*)(injInst + 1);
        int* tarSrc = (int*)(tarInst + 1);

        *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
        *tar = *inj;
#endif
    }
}

运行此代码后,我在我的程序中执行以下代码: LoadedTag.Item.ItemID;其中LoadedTag是应该获得Item Getter的新实现的类,但我得到一个空引用异常,因为函数没有被替换。

但是,如果我在即时窗口中执行此代码,则确实设置了ItemID并调用了拦截函数。

我认为问题是由于垃圾收集器删除了fakeType,它保存了在方法交换期间使用的函数的实际指针。 如果是这样我该如何解决?

提前谢谢!

如果需要请查询完整代码,我会将其上传到Github。

1 个答案:

答案 0 :(得分:-1)

代码不清楚,但是:“ fakeType”与交换指针的位置是否在同一范围内?

如果您更改了作用域,并且fakeType不可用,则垃圾回收器确实会使内存空间无效。

因此,我将在生成假类型以测试是否存在问题后立即尝试移动“交换代码”。

干杯,希望对您有帮助