• 动态IL织入框架Harmony简单入手


    Harmony是一个开放源代码库,旨在在运行时替换、修饰或修改任何现有C#方法。它的主要用在用Mono语言编写的游戏和插件,但是该技术可以与任何.NET版本一起使用。它还照顾对同一方法的多次更改(它们累积而不是覆盖)。

    它为每个原始方法创建DynamicMethod方法,并向其织入代码,该代码在开始和结束时调用自定义方法。它还允许您编写过滤器来处理原始的IL代码,从而可以对原始方法进行更详细的操作。

    文档可以在这里找到。

    • 最新2.0版本终于支持.net core.
    • Harmony支持手动(Patch)和自动(PatchAll)织入
    • 织入位置可以是执行前(Prefix)、执行后(Postfix)和终结嚣(Finalizer),也可以更详细的手动修改IL(Transpiler)
    • 支持构造函数、Getter/Setter、虚/非虚方法、静态方法

    手动模式

    class NoneGenericClass
    {
        private readonly bool _isRunning = true;
        private int _counter = 1;
    
        public int DoSomething()
        {
            Console.WriteLine(nameof(DoSomething));
    
            if (_isRunning)
            {
                _counter++;
            }
            return _counter * 10;
        }
    
        public static int DoSomething2()
        {
            Console.WriteLine(nameof(DoSomething2));
    
            return 3333;
        }
    
        public IEnumerable<int> GetNumbers()
        {
            Console.WriteLine(nameof(GetNumbers));
    
            yield return 1;
            yield return 2;
            yield return 3;
        }
    }
    
    static class NoneGenericClassPatcher
    {
        public static void Patch()
        {
            var harmony = new Harmony(nameof(NoneGenericClassPatcher));
    
            harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.DoSomething)),
                new HarmonyMethod(GetMethod(nameof(MyPrefix))),
                new HarmonyMethod(GetMethod(nameof(MyPostfix))),
                new HarmonyMethod(GetMethod(nameof(MyTranspiler))),
                new HarmonyMethod(GetMethod(nameof(MyFinalizer))));
    
            Console.WriteLine(new NoneGenericClass().DoSomething());
            Console.WriteLine();
    
            harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.GetNumbers)),
                new HarmonyMethod(GetMethod(nameof(MyPrefix))),
                new HarmonyMethod(GetMethod(nameof(PassthroughPostfix))),
                new HarmonyMethod(GetMethod(nameof(MyTranspiler))),
                new HarmonyMethod(GetMethod(nameof(MyFinalizer))));
    
            Console.WriteLine(string.Join(", ", new NoneGenericClass().GetNumbers()));
            Console.WriteLine();
    
            harmony.Patch(typeof(NoneGenericClass).GetMethod(nameof(NoneGenericClass.DoSomething2)),
                new HarmonyMethod(GetMethod(nameof(StaticPrefix))),
                new HarmonyMethod(GetMethod(nameof(MyPostfix))),
                new HarmonyMethod(GetMethod(nameof(MyTranspiler))),
                new HarmonyMethod(GetMethod(nameof(MyFinalizer))));
    
            Console.WriteLine(NoneGenericClass.DoSomething2());
        }
    
        static MethodInfo GetMethod(string name) => typeof(NoneGenericClassPatcher).GetMethod(name, BindingFlags.Static | BindingFlags.Public);
    
        public static bool MyPrefix(out Stopwatch __state, ref bool ____isRunning)
        {
            __state = Stopwatch.StartNew();
            Console.WriteLine($"{nameof(MyPrefix)} {____isRunning}");
    
            return true;
        }
    
        public static bool StaticPrefix(out Stopwatch __state)
        {
            __state = Stopwatch.StartNew();
            Console.WriteLine($"{nameof(StaticPrefix)}");
    
            return true;
        }
    
        public static void MyPostfix(Stopwatch __state, ref int __result, MethodBase __originalMethod)
        {
            Console.WriteLine($"{__state.ElapsedMilliseconds} {__result++}");
            Console.WriteLine(nameof(MyPostfix));
        }
    
        public static IEnumerable<int> PassthroughPostfix(IEnumerable<int> values)
        {
            yield return 0;
            foreach (var value in values)
                if (value > 1)
                    yield return value * 10;
            yield return 99;
            Console.WriteLine(nameof(PassthroughPostfix));
        }
    
        // looks for STDFLD someField and inserts CALL MyExtraMethod before it
        public static IEnumerable<CodeInstruction> MyTranspiler(IEnumerable<CodeInstruction> instructions)
        {
            Console.WriteLine(nameof(MyTranspiler));
            //var found = false;
            foreach (var instruction in instructions)
            {
                //if (instruction.opcode == OpCodes.Stfld && instruction.operand == f_someField)
                //{
                //    yield return new CodeInstruction(OpCodes.Call, m_MyExtraMethod);
                //    found = true;
                //}
                yield return instruction;
            }
            //if (found == false)
            //    ReportError("Cannot find <Stdfld someField> in OriginalType.OriginalMethod");
        }
    
        public static void MyFinalizer(Exception __exception)
        {
            Console.WriteLine($"{nameof(MyFinalizer)} {__exception}");
        }
    }

    自动模式

    public class Annotations
    {
        private readonly bool _isRunning;
    
        public IEnumerable<int> GetNumbers()
        {
            Console.WriteLine(nameof(GetNumbers));
    
            yield return 1;
            yield return 2;
            yield return 3;
        }
    }
    
    [HarmonyPatch(typeof(Annotations))]
    [HarmonyPatch(nameof(Annotations.GetNumbers))]
    public class AnnotationsPatcher
    {
        static AccessTools.FieldRef<Annotations, bool> isRunningRef =
            AccessTools.FieldRefAccess<Annotations, bool>("_isRunning");
    
        public static void Patch()
        {
            var harmony = new Harmony(nameof(AnnotationsPatcher));
    
            harmony.PatchAll();
    
            Console.WriteLine(string.Join(", ", new Annotations().GetNumbers()));
        }
    
        static bool Prefix(Annotations __instance)
        {
            Console.WriteLine("Prefix");
    
            return true;
        }
    
        /// <summary>Not working</summary>
        static IEnumerable<int> Postfix(IEnumerable<int> values)
        {
            yield return 0;
            foreach (var value in values)
                if (value > 1)
                    yield return value * 10;
            yield return 99;
            Console.WriteLine(nameof(Postfix));
        }
    
        // looks for STDFLD someField and inserts CALL MyExtraMethod before it
        public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            Console.WriteLine(nameof(Transpiler));
            //var found = false;
            foreach (var instruction in instructions)
            {
                //if (instruction.opcode == OpCodes.Stfld && instruction.operand == f_someField)
                //{
                //    yield return new CodeInstruction(OpCodes.Call, m_MyExtraMethod);
                //    found = true;
                //}
                yield return instruction;
            }
            //if (found == false)
            //    ReportError("Cannot find <Stdfld someField> in OriginalType.OriginalMethod");
        }
    }

    运行代码

    static void Main(string[] args)
    {
        NoneGenericClassPatcher.Patch();
        Console.WriteLine();
        AnnotationsPatcher.Patch();
    }

    输出结果

    MyTranspiler
    MyPrefix True
    DoSomething
    1 20
    MyPostfix
    MyFinalizer
    21
    
    MyTranspiler
    MyPrefix True
    MyFinalizer
    GetNumbers
    1, 2, 3
    
    MyTranspiler
    StaticPrefix
    DoSomething2
    0 3333
    MyPostfix
    MyFinalizer
    3334
    
    Transpiler
    Prefix
    GetNumbers
    Postfix
    0, 20, 30, 99
  • 相关阅读:
    Python中所有的关键字
    关于selenium的8种元素定位
    对提示框的操作
    selenium+webservice进行百度登录
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled...报错解决
    Vue中使用echarts
    npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142解决方法
    插入排序
    冒泡排序优化
    roject 'org.springframework.boot:spring-boot-starter-parent:XXX' not found 解决
  • 原文地址:https://www.cnblogs.com/qhca/p/12336332.html
Copyright © 2020-2023  润新知