-
Notifications
You must be signed in to change notification settings - Fork 44
Valid patch targets
Harmony is able to patch different kinds of methods, managed or native. However, there are some important notes on what can and cannot be patched with Harmony. This page outlines some certain method types you need to be aware of.
In general, Harmony is able to patch all managed methods -- that is, methods that are defined within .NET itself.
To patch methods you simply use the HarmonyPatch
attribute normally:
// Example of a normal method in a class
class OriginalClass
{
void OriginalMethod()
{
// Some managed code
}
}
// Basic Harmony prefix
[HarmonyPatch(typeof(OriginalClass), "OriginalMethod")]
[HarmonyPrefix]
static void Prefix();
In some cases, the just-in-time (JIT) compiler of the Common Language Runtime (CLR) can replace calls to small methods with the contents of it. As a simple example, consider this code:
int _myInt = 0;
int MyInt
{
get // Small method, compiled to `int get_MyInt()`
{
return _myInt;
}
}
void MyMethod()
{
Console.WriteLine(MyInt); // Compiled to Console.WriteLine(get_MyInt());
}
The getter for MyInt
is very small: it essentially simply contains a return of _myInt
. Thus, when the JIT tries to compile MyMethod
, it will replace the expensive call to get_MyInt
directly with the contents of the getter:
// NOTE: This is pseudocode of how inlining works; the actual process is a bit more tricky than that
void MyMethod()
{
Console.WriteLine(_myInt); // get_MyInt() got inlined and replaced with `_myInt` field directly.
}
As a result, if you were to patch get_MyInt()
, your patch would not work because all calls to the method would get inlined.
If your code runs on Mono (e.g. any Unity game at the moment of writing), Harmony automatically disables inlining, provided you patch the short method before it is called anywhere.
On .NET Framework or .NET Core there is no a concrete fix at the moment.
Sometimes you may want to patch extern
methods like these:
// Native method from kernel32.dll
[DllImport("kernel32.dll")]
static extern void Sleep(int seconds);
// Internall call (e.g. in Unity)
[MethodImpl(MethodImplOptions.InternalCall)]
extern AsyncResult AsyncLoad();
At the time of writing, Harmony 2 supports only transpiling extern
methods without ability to call back the original code.
HarmonyX extends this and allows you to apply prefixes, postfixes, transpilers and finalizers to any method marked with extern
as if it was a normal managed method. The syntax for patching an extern is the same as patching a normal managed method: specify the target with HarmonyPatch
attribute and the patch type.
A simple example:
class MyNativeClass
{
// A simple internal call
// Can also be DllImport for a proper extern call
[MethodImpl(MethodImplOptions.InternalCall)]
extern SomeSpecialType MyICall();
}
// Prefixes work
[HarmonyPatch(typeof(MyNativeClass), "MyICall")]
[HarmonyPrefix]
static void Prefix();
// Transpilers also work
// Note: because externs have no body, `instrs` will get body of a
// special wrapper code that calls the original native method
[HarmonyPatch(typeof(MyNativeClass), "MyICall")]
[HarmonyTranspiler]
static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instrs);
For a full example, check out the unit tests.
At the time of writing this only applies to .NET Core
Just like managed methods, calls to extern
s can be inlined as well. Because of that, HarmonyX cannot reliably patch externs on code running in .NET Core. However, patching is still possible by using MonoMod's NativeDetour
directly. For an example of that refer to MonoMod's NativeDetour unit test.
- Basic usage
-
HarmonyX extensions
1.1. Patching and unpatching
1.2. Prefixes are flowthrough
1.3. Targeting multiple methods with one patch
1.4. Patching enumerators
1.5. Transpiler helpers
1.6. ILManipulators
1.7. Extended patch targets
1.8. New patch attributes -
Extending HarmonyX
2.1. Custom patcher backends -
Misc
4.1. Patch parameters - Implementation differences from Harmony