概念
https://github.com/pardeike/Harmony
C# 的运行时库。
Harmony 提供了大量高阶 API 用来非常方便地打补丁,简单易上手。
补丁分三种:
- Prefix
 - Postfix
 - Transpiler
 
基础用法
public class TargetClass {
    public int TargetMethod(int i)
    {
        return i + 100;
    }
}
[HarmonyPatch(typeof(TargetClass))]
[HarmonyPatch(nameof(TargetClass.TargetMethod))]
public static class Patch_Something
{
    public static void Postfix(ref int __result)
    {
        if (__result > 0)
        {
            result = 0;
        }
    }
}
只需为补丁类标记 HarmonyPatch 特性,Harmony 即可自动收集。
再在任意流程处执行补丁(此处以游戏 RimWorld 为例):
[StaticConstructorOnStartup]
public static class Patch_Apply
{
    static Patch_Apply {
        new Harmony("patcher.id").PatchAll();
    }
}
变体
Harmony 的特性标记使用方法灵活,上例可以有另一种写法:
[HarmonyPatch(typeof(TargetClass), nameof(TargetClass.TargetMethod))]
public static class Patch_Something
{
    [HarmonyPrefix]
    public static void Method(ref __result)
    {
        if (__result > 0)
        {
            result = 0;
        }
    }
}
具体参见 https://harmony.pardeike.net/articles/annotations.html
格式
补丁类必须为静态类。
bool Prefix(...);
Prefix 在原方法之前执行,若返回 true 则跳过原方法。
在 Prefix 中仍可更改原方法的返回值,详见下一节。
void Postfix(...);
Postfix 在原方法之后执行。
IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions);
Transpiler 直接修改原方法的 IL Code。
特殊参数
Prefix 与 Postfix 补丁有一些预设的特殊参数:
| param | desc | 
|---|---|
__instance | 目标类的实例,若目标类为静态类则为 null | 
__result | 目标方法的返回值,可通过 ref 修改 | 
__state | 状态寄存,可在 Prefix 与 Postfix 间传递 | 
NAME | 目标方法的同名传入参数 | 
___NAME | 目标类的同名私有属性 | 
精确制导
指定入参
void Method(char a, int b);
[HarmonyPatch("Method", new Type[]{ typeof(char), typeof(int) })]
指定入参 +
void Method(int a, ref int b, out int c);
[HarmonyPatch(
    "Method",
    new Type[]{ typeof(int), typeof(int), typeof(int) },
    new ArgumentType[]{
        ArgumentType.Normal,
        ArgumentType.Ref,
        ArgumentType.Out
    }
)]
MethodType
[HarmonyPatch("Method", MethodType.Getter)] // get_
[HarmonyPatch("Method", MethodType.Setter)] // set_
[HarmonyPatch("Method", MethodType.Constructor)] // ctor
[HarmonyPatch("Method", MethodType.StaticConstructor)]
[HarmonyPatch("Method", MethodType.Enumerator)] // .MoveNext
Postfix pass through
补丁无法通过 ref 修改 IEnumerable 类型的返回值,此时需要用到 postfix pass through patch。
具体使用方法为令 Postfix 补丁的第一个入参与出参均与函数出参类型相同,则 Harmony 会自动将其识别为 pass through patch。
动态补丁
[HarmonyPatch]
public static class Patch_Dynamic {
    static MethodBase TargetMethod()
    {
        ...
    }
}
[StaticConstructorOnStartup]
public static class Patch_Apply
{
    static Patch_Apply {
        Harmony harmony = new Harmony("patcher.id");
        harmony.Patch(MethodBase, postfix: new HarmonyMethod(Postfix));
    }
}
执行顺序
[HarmonyPriority(Priority.HigherThanNormal)]
[HarmonyBefore("patcher.id")]
[HarmonyAfter("patcher.id")]
Reverse Patcher
反向 Patch 可以提取目标类的函数。某些情况下可以防止过多使用反射导致的性能问题。
用的不多,略过。