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. KC开发组官方网站

    KC开发组,简称KC,又叫KC Team,是Kong Che Team(空车开发组)的缩写 是由ordosyz(某乡村小学)的CSP2019退役OIer(信息学奥林匹克竞赛选手) 于2020年2月23 ...

  2. Codeforces Round #616 (Div. 2) C. Mind Control

    题目链接:http://codeforces.com/contest/1291/problem/C 思路: 我们可以很容易想到,只有前m-1个人才能影响m的选择的大小,后面的人无法影响. 如果所有人都 ...

  3. Codeforce 25A - IQ test (唯一奇偶)

    Bob is preparing to pass IQ test. The most frequent task in this test is to find out which one of th ...

  4. H3C虚拟化技术

    一.IRF简单介绍 IRF(Intelligent Resilient Framework,智能弹性架构)是H3C自主研发的软件虚拟化技术.它的核心思想是将多台设备连接在一起,进行必要的配置后,虚拟化 ...

  5. [ZJOI2007] 仓库建设 - 斜率优化dp

    大脑真是个很优秀的器官,做事情之前总会想着这太难,真的逼着自己做下去,回头看看,其实也不过如此 很朴素的斜率优化dp了 首先要读懂题目(我的理解能力好BUG啊) 然后设\(dp[i]\)表示处理完前\ ...

  6. PHP 实现时间戳转化为几分钟前、几小时前等格式

    //发布时间提示 function get_last_time($time) { // 当天最大时间 $todayLast = strtotime(date('Y-m-d 23:59:59')); $ ...

  7. JS模板引擎-Mustache模板引擎使用实例1-表格树

    1 使用实例代码 1.jsp代码 <!DOCTYPE html> <html lang="zh-CN"> <head> <title> ...

  8. PLSQL官网下载地址

    记录一下 https://www.allroundautomations.com/registered/plsqldev.html

  9. org.apache.catalina.connector.ClientAbortException: java.io.IOException: 您的主机中的软件中止了一个已建立的连接。

    日志文件中有“java.io.IOException: 您的主机中的软件中止了一个已建立的连接.”错误 org.apache.catalina.connector.ClientAbortExcepti ...

  10. Reverse is Multiplex, You Need PinTools.

    Read this slide: pin_in_CTF.pdf And this link: pin_in_CTF