动态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 = 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311681.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows 仍在设置此设备的类配置。 (代码 56)_谷歌发布Flutter Alpha:支持Windows

老孟导读&#xff1a;Windows来了&#xff0c;Mac、Linux、Web还远吗&#xff1f; 本文翻译自https://medium.com/flutter/announcing-flutter-windows-alpha-33982cd0f433 我们的使命是为开发人员提供一个开源&#xff0c;高生产率的框架&#xff0c;以便在任何平台上构建漂亮…

word List39

word List39 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

不同路径 II-dp

题目背景 一个机器人位于一个 n x m 网格的左上角 机器人每次只能向下或者向右移动一步。它试图达到网格的右下角 题目描述 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径&#xff1f; 网格中的障碍物和空位置分别用 1 和 0 来表示。 输入格式 第一行两…

.NET Core开发实战(第5课:依赖注入:良好架构的起点)--学习笔记(下)

05 | 依赖注入&#xff1a;良好架构的起点注册完毕之后&#xff0c;想替换某些组件的某些部分时&#xff0c;可以使用 Replace 和 RemoveAllservices.AddSingleton<IOrderService>(new OrderService()); services.Replace(ServiceDescriptor.Singleton<IOrderService,…

python正态分布函数_python3-正态分布

loc 平均值 scale (scale) 标准差 pdf(x, loc0, scale1) 正态分布&#xff08;Normal distribution&#xff09;&#xff0c;也称“常态分布”&#xff0c;又名高斯分布&#xff08;Gaussian distribution&#xff09;&#xff0c;最早由A.棣莫弗在求二项分布的渐近公式中得到。…

word List40

word List40 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

dp巩固训练

按顺序从上往下刷即可&#xff01;&#xff01;&#xff01; 目录&#xff1a; 青蛙跳台阶[蓝桥杯]字母组串整数的分法不同路径 I网格路径最小数字和AcWing 1015.摘花生不同路径II01背包问题数字和为sum的方法数-01背包计数问题m个苹果放入n个盘子问题 数的划分[蓝桥杯][算法…

int 范围_Java学习之随机生成5个(范围1——33)不同数字的思考

昨天做了一道福彩双色球的简单程序题&#xff0c;一开始不懂双色球的玩法&#xff0c;楼主我从来不买彩票的&#xff0c;( ╯□╰ )。所以随机生成某个范围的5个数字&#xff08;数字可以重复&#xff09;&#xff0c;那么很简单&#xff0c;一个循环就可以搞定了&#xff0c;但…

asp.net core 使用newtonsoft完美序列化WebApi返回的ValueTuple

由于开发功能的需要,又懒得新建太多的class,所以ValueTuple是个比较好的偷懒方法,但是,由于WebApi需要返回序列化后的json,默认的序列化只能将ValueTuple定义的各个属性序列化成Item1...n但是微软还是良心的为序列化留下入口,编译器会在每个返回ValueTuple<>的函数或者属…

高级数据结构---优先队列

高级数据结构—优先队列 原理&#xff1a;参考趣学数据结构 代码&#xff1a; #include <stdio.h> #include <stdlib.h> int r[] { -1,1,4,590,4,2,8,7,5,89,67,5,2,1,67,86,54 };//存储要排序的数,第一个元素不存储元素赋值为-1 int length sizeof(r) / size…

[蓝桥杯2017初赛]贪吃蛇长度-模拟(水题)

题目描述 小明在爷爷的私人收藏馆里找到一台老式电脑。居然没有图形界面&#xff0c;只能用控制台编程。 经过小明的一阵摸索&#xff0c;神奇地设计出了控制台上的贪食蛇游戏。 如下图&#xff0c;是游戏时画面截图。 其中&#xff0c;H表示蛇头&#xff0c;T表示蛇尾。#表示…

蓝屏(BSOD)转储设置,看本文就够了!

前言 我们在内核转储&#xff0c;开抓啦&#xff01;这篇文章里介绍了一个关键的系统设置。设置好后可以让系统在蓝屏&#xff08;Blue Screen of Death&#xff0c;简称 BSOD&#xff09;的时候自动保存转储文件。当时只是简单的介绍了设置步骤&#xff0c;本文力求详细的介绍…

算法---字符串顺序平移

算法—字符串顺序平移 原理&#xff1a;矩阵的转置思想 代码&#xff1a; #include <stdio.h> #include <stdlib.h> void swap(char *a,int i, int j) {//交换二个变量的值char temp a[i];a[i] a[j];a[j] temp; } void invert(char *a,int s, int e) {//对称…

delphi中的函数传参如何传枚举参数_Python基础笔记Day05函数

Python函数 function函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。函数能提高应用的模块性&#xff0c;和代码的重复利用率。Python提供了许多内建函数&#xff0c;比如print()。但你也可以自己创建函数&#xff0c;这被…

[蓝桥杯2018初赛]日志统计-双指针

代码如下&#xff1a; #include <iostream> #include <algorithm> using namespace std; typedef pair<int, int>PII; #define x first #define y second const int N 100010; bool st[N]; int n, d, k; PII a[N]; int cnt[N];int main() {cin >> n …

不要错过这轮疫情的“洗牌”机会

大家好&#xff0c;我是Z哥。今天和大家随便聊聊天。这次的疫情对我们所有人影响都很大&#xff0c;除了让你心怀忐忑的过了个春节之外&#xff0c;呆在家的时间对很多人来说也是格外的长。这样的突发事件&#xff0c;除了能看出不同公司之间应对突发状况的能力差异之外&#x…

算法---查找倒数第k个链表的值

算法—查找倒数第k个链表的值 代码&#xff1a; link.h #pragma once #define elemType int #include<stdlib.h> typedef struct link {elemType data;struct link * next; }link; bool initLink(link* &Link) {//初始化链表Link (link*)malloc(sizeof(link));Li…

mysql boolean_产品操作MySQL第7篇 – 运算符 – IS NULL

本资料为产品岗位作为日常工作参考&#xff0c;语言口语化At 2019/4/27 By David.Yang介绍什么是IS NULL IS NULL作为一种运算符&#xff0c;用来对数据表中的NULL值数据进行过滤。语法target IS NULLtarget值为NULL&#xff0c;则表达式返回TRUE&#xff0c;否则返回FALSE。MY…

[蓝桥杯2016初赛]交换瓶子

题目描述 有N个瓶子&#xff0c;编号 1 ~ N&#xff0c;放在架子上。 比如有5个瓶子&#xff1a;2 1 3 5 4&#xff0c;要求每次拿起2个瓶子&#xff0c;交换它们的位置。 经过若干次后&#xff0c;使得瓶子的序号为&#xff1a;1 2 3 4 5 对于这么简单的情况&#xff0c;显然…

微软开源Scalar,提升操作巨型Git仓库的速度

Git 属于分布式版本控制系统&#xff0c;默认情况下&#xff0c;每个 Git 仓库都具有整个历史记录的完整文件副本。即便是中等规模的开发团队也会产生数千个提交&#xff0c;每个月向仓库添加几百兆的数据。而随着仓库的占用空间增加&#xff0c;Git 难以管理所有数据&#xff…