动态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 = ; 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简单入手的更多相关文章
- 【开源】.Net Aop(静态织入)框架 BSF.Aop
BSF.Aop .Net 免费开源,静态Aop织入(直接修改IL中间语言)框架,类似PostSharp(收费): 实现前后Aop切面和INotifyPropertyChanged注入方式. 开源地址: ...
- Spring AOP 之编译期织入、装载期织入、运行时织入(转)
https://blog.csdn.net/wenbingoon/article/details/22888619 一 前言 AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP ...
- AOP的核心:代理与织入
分为两步: 1.动态生成代理类: 2.织入: 2.6 织入(Weaving) 织入是将增强添加到目标的具体连接点上的过程 . AOP 织入方式: 方式 实现 应用编译期织入 特殊的 Java 编译器. ...
- AOP 动态织入的.NET实现
AOP(面向切面编程:Aspect Oriented Programming)为诸如日志记录.性能统计.安全控制.事务处理.异常处理等与具体业务逻辑无关,却需要在全局范围进行执行的功能提供了一种良好重 ...
- 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)
一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...
- 30个类手写Spring核心原理之AOP代码织入(5)
本文节选自<Spring 5核心原理> 前面我们已经完成了Spring IoC.DI.MVC三大核心模块的功能,并保证了功能可用.接下来要完成Spring的另一个核心模块-AOP,这也是最 ...
- .NET静态代码织入——肉夹馍(Rougamo)
肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...
- .NET静态代码织入——肉夹馍(Rougamo) 发布1.2.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- Spring的LoadTimeWeaver(代码织入)
在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入.类加载期织入和运行期织入.编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中:而类加载期织入则指通过特 ...
随机推荐
- linux 搭建python虚拟环境
requirements.txt 包含paramiko,pysfp.setuptools,适用python版本3.6.6+ 前提编译安装python wget wget https://www.pyt ...
- matplotlib数组转图片的一些坑
最近用matplotlib遇到了一些坑,记录一下. 图片转数组 import matplotlib.pyplot as plt im_file='test_image.jpg' img=plt.imr ...
- IDEA 接口调试插件 HTTP Client
界面客户端 使用手册 https://www.jetbrains.com/help/idea/testing-restful-web-services.html 打开方式 Tools -> HT ...
- 专项:Vuejs面试题集合
参考网络资源:https://segmentfault.com/a/1190000012315822 1.active-class是哪个组件的属性? 答:active-class是vue-router ...
- vue制作滚动条幅-跑马灯效果实例代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- LaTeX技巧006:使用pdfLaTeX时,添加PDF文件属性的方法
PDF文件中含有标题.主题.作者.关键字等属性.这些属性,在Acrobat Reader或者Foxit Reader中可以通过”文件”菜单下的”属性”查看,在Acrobat Read中还可以使用Ctr ...
- Notepad++ 个人洁癖
插件: JSON Viewer 可以以树的形式查看JSON,同时可以格式化JSON,增加缩进. NppExport 可以高亮复制 下载地址: https://github.com/chcg/NPP_E ...
- 将本地文件git到远程github仓库
使用git管理项目是开发人员必备的技能,下面就介绍如何使用git将本地文件管理并且同步到github上面. 小白可以参考 创建SSH-key并且在github中添加认证 在本地用户的.ssh文件下面生 ...
- python面试的100题(14)
32.请写出一个函数满足以下条件 该函数的输入是一个仅包含数字的list,输出一个新的list,其中每一个元素要满足以下条件: 1.该元素是偶数 2.该元素在原list中是在偶数的位置(index是偶 ...
- 创建spring boot 项目
1.新建Spring Starter Project(需要连接外网) 2.选择web 3.点击完成,生成maven项目 pom文件默认有一些依赖,但是有一个地方报错,<parent>节点报 ...