Skip to content

Method patches

Geoffrey Horsington edited this page Jan 22, 2020 · 3 revisions

Method patches work by applying HarmonyPatch attribute to specific methods.

Basic example

Here's an example of a class patch:

public class OriginalType
{
    public static void TargetMethod()
    {
        Console.WriteLine("Target method!");
    }
}

public class MyPatch
{

    // Specifies the method to be a patch that targets OriginalType.TargetMethod
    [HarmonyPatch(typeof(OriginalType), "TargetMethod")]
    [HarmonyPrefix]
    public static void Prefix()
    {
        // Code to run at the start of the original method
        Console.WriteLine("Hello");
    }
    
    // Specifies the method to be a patch that targets OriginalType.TargetMethod
    [HarmonyPatch(typeof(OriginalType), "TargetMethod")]
    [HarmonyPostfix]
    public static void Postfix()
    {
        // Code to run after the original method
        Console.WriteLine("Bye");
    }
}

Class patches are applied by calling Harmony.PatchAll(Type) to which you pass the type that contains your patch methods. After you apply the patch, calling OriginalType.TargetMethod will print

Hello
Target method!
Bye

To specify multiple targets, you can apply multiple HarmonyPatch attributes on a single patch:

// Patches both TargetMethod and TargetMethod2
[HarmonyPatch(typeof(OriginalType), "TargetMethod")]
[HarmonyPatch(typeof(OriginalType), "TargetMethod2")]
[HarmonyPrefix]
public static void Prefix()

Patch methods

Method patches work on a per-method basis and only with attributes. As such, no lifetime methods are available in method patches.

Here is a list of all methods supported by class patches.

Note about syntax: the [] brackets inside the method arguments denote optional arguments.

Prefix

[HarmonyPrefix]
static (void|bool) MyPrefix([...])

This method defines the code that is executed before the original method. Prefixes can optionally return a boolean: if prefix returns true, the original method code is executed; if prefix returns false, the original method code is skipped.

Difference between Harmony and HarmonyX: HarmonyX will run all prefixes even if one of them returns false. Refer to differences from Harmony for more info.

Prefixes can accept various parameters. Refer to Patch parameters for a list of all supported patch parameters.

Postfix

[HarmonyPostfix]
static void MyPostfix([...])

// Passthrough prefix (receives return value as first parameter, returns new return value)
[HarmonyPostfix]
static T MyPostfix([T result, ...])

This method defines the code that is executed after the original method. All postfixes always get executed even if the original method code was skipped.

There are two types of postfixes: normal and passthrough. Normal postfixes have return type void and behave like prefixes.
Passthrough postfixes have a return value and an optional return argument the types of which are the same target method return value. Passthrough postfixes receive the return value of the original method and must return either the same value or a modified one.

Postfixes can accept various parameters. Refer to Patch parameters for a list of all supported patch parameters.

Finalizer

// Rethrowing finalizer
[HarmonyFinalizer]
static void MyFinalizer([...])

// Throwing finalizer
[HarmonyFinalizer]
static Exception MyFinalizer([...])

Finalizers allow you to catch, throw and suppress exceptions in a method. Finalizers run before postfixes and only if original method wasn't skipped. Finalizers also run immediately if an exception occurs inside the original method.

Finalizers can accept various parameters. Refer to Patch parameters for a list of all supported patch parameters.

Finalizers are an advanced topic. Refer to Finalizers guide for more info.

Transpiler

[HarmonyTranspiler]
static IEnumerable<CodeInstruction> MyTranspiler(IEnumerable<CodeInstruction> instr[, ...])

This method defines the transpiler that modifies the code of the original method. Use this in the advanced case where you want to modify the original methods IL codes.

This method must always have at least one parameter of type IEnumerable<CodeInstruction> that will receive a list of IL instructions of the original methods. The transpiler must return a new IEnumerable<CodeInstruction> that will be used as the modified code of the original method.

Transpilers are an advanced topic. Refer to Transpilers guide for more info.