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 = ; public int DoSomething()
{
Console.WriteLine(nameof(DoSomething)); if (_isRunning)
{
_counter++;
}
return _counter * ;
} public static int DoSomething2()
{
Console.WriteLine(nameof(DoSomething2)); return ;
} public IEnumerable<int> GetNumbers()
{
Console.WriteLine(nameof(GetNumbers)); yield return ;
yield return ;
yield return ;
}
} 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 ;
foreach (var value in values)
if (value > )
yield return value * ;
yield return ;
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 ;
yield return ;
yield return ;
}
} [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 ;
foreach (var value in values)
if (value > )
yield return value * ;
yield return ;
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 MyPostfix
MyFinalizer MyTranspiler
MyPrefix True
MyFinalizer
GetNumbers
, , MyTranspiler
StaticPrefix
DoSomething2 MyPostfix
MyFinalizer Transpiler
Prefix
GetNumbers
Postfix
, , ,

动态IL织入框架Harmony简单入手的更多相关文章

  1. 【开源】.Net Aop(静态织入)框架 BSF.Aop

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

  2. Spring AOP 之编译期织入、装载期织入、运行时织入(转)

    https://blog.csdn.net/wenbingoon/article/details/22888619 一   前言 AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP ...

  3. AOP的核心:代理与织入

    分为两步: 1.动态生成代理类: 2.织入: 2.6 织入(Weaving) 织入是将增强添加到目标的具体连接点上的过程 . AOP 织入方式: 方式 实现 应用编译期织入 特殊的 Java 编译器. ...

  4. AOP 动态织入的.NET实现

    AOP(面向切面编程:Aspect Oriented Programming)为诸如日志记录.性能统计.安全控制.事务处理.异常处理等与具体业务逻辑无关,却需要在全局范围进行执行的功能提供了一种良好重 ...

  5. 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)

    一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...

  6. 30个类手写Spring核心原理之AOP代码织入(5)

    本文节选自<Spring 5核心原理> 前面我们已经完成了Spring IoC.DI.MVC三大核心模块的功能,并保证了功能可用.接下来要完成Spring的另一个核心模块-AOP,这也是最 ...

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

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

  8. .NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0

    肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...

  9. Spring的LoadTimeWeaver(代码织入)

    在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入.编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中:而类加载期织入则指通过特 ...

随机推荐

  1. 0级搭建类002-Oracle Linux 8.x安装(OEL 8.0) 公开

    项目文档引子系列是根据项目原型,制作的测试实验文档,目的是为了提升项目过程中的实际动手能力,打造精品文档AskScuti. 项目文档引子系列目前不对外发布,仅作为博客记录.如学员在实际工作过程中需提前 ...

  2. java学习笔记之反射—反射和工厂模式

    简单工厂模式又称为静态工厂方法模式,它是由工厂对象来决定要创建哪一种类的实例化对象. 静态工厂代码: class Factory{ private Factory() {} public static ...

  3. 2019-08-23 纪中NOIP模拟A组

    T1 [JZOJ2908] 矩阵乘法 题目描述 给你一个 N*N 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 K 小数. 数据范围 对于 $20\%$ 的数据,$N \leq 100$,$Q ...

  4. 【vue 权威指南】 学习笔记 一

    内容简介 vue.js 是一个用来开发Web界面的前端库. 1.vue.js 是什么 vue.js 是一个构建数据驱动的web界面的库,vue.js 通过简单的API提供高效的数据绑定和灵活的组件系统 ...

  5. AcWing 11. 背包问题求方案数

    //g[i,j]表示f[i,j]取最大值的方案数目 //体积最多是j 全部为0,v>=0 //体积恰好为j f[0][0]=0,f[i]=无穷,v>=0 //体积至少是j f[0][0]= ...

  6. 洛谷P1308 统计单词数

    原题链接:https://www.luogu.org/problem/P1308 #include<iostream> #include<cstring> #include&l ...

  7. 【你不知道的javaScript 上卷 笔记1】 javaScript 是如何工作的?

    一.什么是作用域? 作用域是用来存储变量以及方便寻找变量的一套规则. 二.javaScript 编译过程(编译发生在代码执行前的几微妙) 分词/词法分析(Tokenizing/Lexing)-> ...

  8. Laradock + tp5 + nginx 配置虚拟机域名始终跳转首页/502报错

    laradock默认配置文件如下: 配置运用于本地windows+phpstudy 部署的laravel项目未出现问题,如下: server { listen ; listen [::]:; serv ...

  9. python3运行调用htmltestrunner时,报错UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0

    之前解决过一次,又忘了,这次写下来了..百度没有的,跟我环境有关! 环境:自动化运行环境python3.6.5 上期说到了,写了一个bat来运行runallcase.py. 但是双击运行却报错:Uni ...

  10. Date/Math/String对象的函数

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...