如何用DynamicMethod

问题描述

在帖子Replacing C# method of declaring type which implements an interface and inherits from base中,我知道如何将方法指针更改为方法的另一个指针或Action / Func。但是我需要将其替换为指向Dynamicmethod的指针。

提到的帖子中的工作示例,其中添加了一种方法,稍后将使用该方法检查其是否与Dynamicmethod一起使用:

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Foo.Bar {

    public interface IFoo {
        string Apple();
    }

    public class Bar {

        protected virtual object One() {
            return null;
        }

        protected virtual object Two() {
            return null;
        }

        protected virtual object Three() {
            return null;
        }

        // New
        public override int VirtualTestMethod()
        {
            return 10;
        }

        /* Uncomment this to generate a null reference */
        //protected virtual object Four() {
        //    return null;
        //}

    }

    public class Foo : Bar,IFoo {

        public string Apple() {
            return "Apple";
        }

        public string Orange() {
            return "Orange";
        }

        // New
        public override int VirtualTestMethod()
        {
            return 0;
        }

        /* Uncommenting this fixes the null reference */
        //public override int GetHashCode() {
        //    throw new NotImplementedException();
        //}

        public void ReplaceMethod(Delegate targetmethod,Delegate replacementMethod) {

            MethodInfo methodToReplace = targetmethod.Method;
            MethodInfo methodToInject = replacementMethod.Method;

            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            if (methodToReplace.IsVirtual)
                ReplaceVirtualInner(methodToReplace,methodToInject);
            else
                ReplaceStandard(methodToReplace,methodToInject);

        }

        // New
        public void ReplaceMethod(MethodInfo methodToReplace,MethodInfo methodToInject)
        {

            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            if (methodToReplace.IsVirtual)
                ReplaceVirtualInner(methodToReplace,methodToInject);
        }

        private void ReplaceStandard(MethodInfo methodToReplace,MethodInfo methodToInject) {

            IntPtr targetPtr = methodToInject.MethodHandle.Value;
            IntPtr replacePtr = methodToReplace.MethodHandle.Value;

            unsafe
            {
                if (IntPtr.Size == 4) {

                    int* inj = (int*)replacePtr.ToPointer() + 2;
                    int* tar = (int*)targetPtr.ToPointer() + 2;

                    if (Debugger.IsAttached) {

                        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;
                    }
                }
                else {

                    long* inj = (long*)replacePtr.ToPointer() + 1;
                    long* tar = (long*)targetPtr.ToPointer() + 1;

                    if (Debugger.IsAttached) {

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

                        long* injsrc = (long*)(injInst + 1);
                        long* tarSrc = (long*)(tarInst + 1);

                        *tarSrc = (((long)injInst + 5) + *injsrc) - ((long)tarInst + 5);
                    }
                    else {
                        *tar = *inj;
                    }
                }
            }


        }

        private void ReplaceVirtualInner(MethodInfo methodToReplace,MethodInfo methodToInject) {

            unsafe
            {
                UInt64* methodDesc = (UInt64*)(methodToReplace.MethodHandle.Value.ToPointer());
                int index = (int)(((*methodDesc) >> 32) & 0xFF);

                if (IntPtr.Size == 4) {
                    uint* classstart = (uint*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
                    classstart += 10;
                    classstart = (uint*)*classstart;

                    uint* tar = classstart + index;
                    uint* inj = (uint*)methodToInject.MethodHandle.Value.ToPointer() + 2;

                    if (Debugger.IsAttached) {

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

                        uint* injsrc = (uint*)(injInst + 1);
                        uint* tarSrc = (uint*)(tarInst + 1);

                        *tarSrc = (((uint)injInst + 5) + *injsrc) - ((uint)tarInst + 5);
                    }
                    else {
                        *tar = *inj;
                    }

                }
                else {

                    ulong* classstart = (ulong*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
                    classstart += 8;
                    classstart = (ulong*)*classstart;

                    ulong* tar = classstart + index;
                    ulong* inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;

                    if (Debugger.IsAttached) {

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

                        ulong* injsrc = (ulong*)(injInst + 1);
                        ulong* tarSrc = (ulong*)(tarInst + 1);

                        *tarSrc = (((ulong)injInst + 5) + *injsrc) - ((ulong)tarInst + 5);
                    }
                    else {
                        *tar = *inj;
                    }
                }
            }
        }
    }
}

工作用法示例(也可与其他ReplaceMethod-过载一起使用

    Foo.Bar.Foo foo = new Foo.Bar.Foo();

    foo.ReplaceMethod(
        ((Func<string>)foo.Apple),((Func<string>)foo.Orange)
    );

    var result = foo.Apple(); // this is "Orange" :)

不起作用的示例:

    Foo.Bar.Foo foo = new Foo.Bar.Foo();

    var methodInfo = foo.GetType().getmethod("VirtualTestMethod",(BindingFlags)(-1));

    var dm = new Dynamicmethod("",typeof(int),new Type[]{});
    var il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldc_I4,25);
    il.Emit(OpCodes.Ret);

    foo.ReplaceMethod(
        methodInfo,dm.Method
    );

    var result = foo.VirtualTestMethod(); // this should be 25

但是,如果我尝试将其替换为Dynamicmethod,那么它将不起作用,因为它没有MethodHandle。如果我使用以下代码片段从MethodHandle获取Dynamicmethod并将其用于方法中,则该方法将无效。摘录摘自该SO answer

private static readonly Func<Dynamicmethod,RuntimeMethodHandle> getDynamicHandle = Delegate.CreateDelegate(
        typeof(Func<Dynamicmethod,RuntimeMethodHandle>),typeof(Dynamicmethod).getmethod("getmethodDescriptor",BindingFlags.Instance | BindingFlags.NonPublic)
    ) as Func<Dynamicmethod,RuntimeMethodHandle>;

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)