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())); //BUG:有Finalizer方法时PassthroughPostfix不生效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 itpublic 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 itpublic 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
20
MyPostfix
MyFinalizerMyTranspiler
MyPrefix True
MyFinalizer
GetNumbers
1, 2, 3MyTranspiler
StaticPrefix
DoSomething2
3333
MyPostfix
MyFinalizerTranspiler
Prefix
GetNumbers
Postfix
0, 20, 30, 99

动态IL织入框架Harmony简单入手相关推荐

  1. .Net Aop(静态织入)框架 BSF.Aop

    BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...

  2. Python,Scrapy 爬虫框架,简单入手的案例(适合初学者阶段入手的案例)

    [项目目录] 1) 分析目标网站 2) 创建项目 3) 保存数据csv 1) 分析目标网站 目标网址 我们先分析页面是怎样的,这个网页比较简单,适合新手练练. 网 网页结构也比较简单,所以我们就用CS ...

  3. aspectj切面织入

    一.介绍 AspectJ 是一个 AOP 的具体实现框架.AOP(Aspect Oriented Programming)即面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给 ...

  4. 有关AOP术语(织入、增强等)的个人理解

    2021.4.11 阅读<精通Spring4.x企业应用开发实战>一书做笔记 下图为本人理解的AOP术语韦恩图,由于引介跟增强在同一个位置,没在图上标出. 连接点(Joint Point) ...

  5. 关于springAOP的织入

    织入是将增强添加对目标类具体连接点上的过程.AOP像一台织布机,将目标类.增强或引介通过AOP这台织布机天衣无缝地编织到一起.根据不同的实现技术,AOP有三种织入的方式: a.编译期织入,这要求使用特 ...

  6. AOP相关术语(连接点、切入点、通知、引介、目标对象、织入、代理、切面)

    Joinpoint(连接点): 所谓连接点是指那些被拦截到的点.在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.(通俗理解:业务层接口的所有方法都叫连接点) Pointcu ...

  7. .NET静态代码织入——肉夹馍(Rougamo)

    肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...

  8. 从Spring源码探究AOP代码织入的过程

    随着不断地使用Spring,以及后续的Boot.cloud,不断的体会到这个拯救Java的生态体系的强大,也使我对于这个框架有了极大的好奇心,以至于产生了我为什么不能写一个这样的框架的思考. 通过自学 ...

  9. Spring 揭秘之Spring AOP一世(2)织入实现

    文章目录 Spring AOP一世 Spring AOP的织入 与ProxyFactory打交道 基于接口的代理 基于类的代理 Introduction的织入 ProxyFactory的本质 容器中的 ...

最新文章

  1. 菜鸟级三层框架(EF+MVC)项目实战之 系列一 EF零基础创建领域模型
  2. 为什么我们做分布式的使用都是用Redis?
  3. 多边形面积(Area_Of_Polygons)
  4. S2-016、S2-017
  5. 不错的向上滚动广告代码
  6. cp oracle,Oracle ASM使用asmcmd中的cp命令来执行远程复制
  7. 敏感型人格的特征,怎么改变敏感型性格?
  8. 小米CC9BL解锁、root方法测试
  9. tassel软件使用linux,科学网—TASSEL 关联分析软件 起步教程 - 邓飞的博文
  10. 《三国演义》人物出场实例详解
  11. c 语言编程规则,C语言编程之 makfile规则.doc
  12. windows常用系统命令
  13. 用python读取tif格式图像
  14. 去掉WebView中的白色背景
  15. 阿里内网最新发布“M8”级Java面试笔记,助力金九银十
  16. 基础知识 | node js基础知识
  17. 基金的估值原来这么简单,一文看懂
  18. Log BERT 日志异常检测
  19. 分享文章:如何提高你的学习能力,也许对你会有用
  20. JavaScript基础——使用Canvas画图

热门文章

  1. php Collection类的设计
  2. 【转】一个关于fork()的笔试题,考了好几遍,终于找到答案了
  3. Mac OS使用技巧之十三:Finder中标记的使用
  4. dropbox mac_如何在Windows或Mac上启动时阻止Dropbox打开
  5. windows7黑屏修复_如何在Windows 10更新后修复黑屏
  6. minecraft服务器_如何启动自己的Minecraft服务器进行多人游戏
  7. chrome 网页重新加载_在Chrome中为各个网页设置自定义重新加载时间
  8. kotlin 初始化数组
  9. python基础知识-8-三元和一行代码(推导式)
  10. 装饰一个类及内部方法