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. virtual box虚拟机跨物理机迁移

    VirtualBox保存虚机数据的是vdi文件,但是直接拷贝在新的VirtualBox中不能打开,因为每个vdi文件都包含了一个uuid的数据,如果直接拷贝,uuid在新的VirtualBox中是不能 ...

  2. 曼孚科技:AI自然语言处理(NLP)领域常用的16个术语

    ​自然语言处理(NLP)是人工智能领域一个十分重要的研究方向.NLP研究的是实现人与计算机之间用自然语言进行有效沟通的各种理论与方法. 本文整理了NLP领域常用的16个术语,希望可以帮助大家更好地理解 ...

  3. ssh排错思路

    telnet远程22端口refused 先netstat  -utnlp  看一下22端口监听状态(一般都不在监听) 然后systemctl  restart sshd 如果有报错,执行sshd  - ...

  4. P1002 过河卒【dp】

    P1002 过河卒 题目描述 棋盘上AAA点有一个过河卒,需要走到目标BBB点.卒行走的规则:可以向下.或者向右.同时在棋盘上CCC点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制 ...

  5. Failed to restart docker.service: Unit not found 镜像加速

    解决方案 以前的安装残留 重新安装 find / -name "docker*" centos8 添加软件源信息 yum-config-manager --add-repo htt ...

  6. 理解 Oracle 多租户体系中(12c,18c,19c)Grant授权作用域范围

    本篇探讨以下几个问题:你可提前猜测下面6个场景语句中,哪几个授权可以成功执行? 1. 在CDB级别中对用户进行授权,不带 container 子句的效果: 2. 在CDB级别中对用户进行授权,带 co ...

  7. 录入规则文件名到CSV文件

    import os import sys import csv # 导出到csv文件 def export_to_csv(datas): with open('export.csv', 'w', ne ...

  8. LVS+Nginx(LVS + Keepalived + Nginx安装及配置)

    (也可以每个nginx都挂在上所有的应用服务器)  nginx大家都在用,估计也很熟悉了,在做负载均衡时很好用,安装简单.配置简单.相关材料也特别多. lvs是国内的章文嵩博士的大作,比nginx被广 ...

  9. Windows ThinPC 7 部署后续设置与本地化

    还原注销背景 %system32%\oobe\info\backgrounds 删除该目录下所有文件后变为wes7背景 24时制与非UNICODE字符乱码解决 Control Panel \ Regi ...

  10. AtCoder Beginner Contest 153 题解

    目录 AtCoder Beginner Contest 153 题解 A - Serval vs Monster 题意 做法 程序 B - Common Raccoon vs Monster 题意 做 ...