What's new in v3
These guides describe Harmony 3 and its new APIs on the v3 branch; the 2.x documentation covers Harmony 2.
Patch inside a method with Infix
Use prefixes, postfixes, and finalizers around selected operations in an outer method. Select calls, property access, field reads and writes, construction, or literal loads. Other callers are unaffected by that Infix.
- Inner prefixBefore this operation
- OperationCall, read, write, create, or load
- Inner postfixAfter this operation
The orange border marks the selection. Calls from other methods are unaffected by this Infix. If an exception escapes the selection, normal exception handling in the outer method decides what happens next.
Start with the working example, then explore recipes and instruction edits and limits.
Work with generated bodies
InfixOuterBody.Auto resolves supported iterator and async methods to their generated execution body. ArgumentMode.Captured accesses live captured variables; ArgumentMode.Persistent keeps patch-owned values across suspensions within one execution.
Read about generated bodies and persistent state, including the supported compiler protocols and cleanup rules.
.NET 11 support
Harmony 3 adds .NET 11 support, verified with RC3. .NET 11 runtime async remains unsupported and throws NotSupportedException when patched or reverse-patched. Ordinary Task-returning methods and compiler-generated async state machines remain supported, but patches on them do not reach callers compiled with runtime-async, see the edge cases.
Host Harmony in a runtime
A runtime that can replace method bodies can install Harmony's patches itself. MethodBodyPatching registers an eligibility check, a baseline reader, and an installer. An accepted generic definition gets one body for all instantiations. Declined targets keep the detour. Read how to host Harmony.
The host contract includes these rules:
- Register all parts before Harmony pins a host-owned target. Late non-null registration throws. A failed host installation retains its selection without publishing candidate patch records.
- Installers must not call patch, unpatch, reverse-patch, or rebuild operations. Harmony rejects re-entry into the same target or generic family.
- Definition-wide bindings require a guaranteed assignment from the open source. Closed bindings and passthrough results validate closed types. Foreign generic parameters in member operands fail before installation.
- Directly constructed callbacks are rejected for targets installed through a definition. Register a generic callback definition, or use a factory to preserve fixed arguments.
- Within a generic family, equal priority and index put the definition's patch first, whichever member started the rebuild.
- Generic family bridges reject function-pointer signatures. Reverse patches into host-owned generic targets are unsupported. Constructors installed through a generic family return null from
Patch(). - Rebuilding a definition uses its members' records when it has no record of its own. Successful ordinary installation supersedes an earlier reverse recipe.
- The importer accepts
KeyValuePair<MemberInfo, bool>withtruefor self-instantiated generic members. It must preserve MemberRef/TypeSpec and MethodSpec semantics. Refused identity operands such asldtokencannot use forwarding stand-ins. - Generated closed helpers reuse permanent stand-ins by original, helper site, and signature. Refused open generic helpers can use permanent images, but those images cannot reference collectible dependencies.
- Body export is unavailable in Harmony's
net35andnetstandard2.0builds. Generic helper generation requires collectibleAssemblyLoadContextsupport, so .NET Framework and Mono refuse Infix patches on a generic method definition before installation.
Runtime detection
AccessTools.IsNetCoreRuntime is now true on .NET Core 3.x and on .NET 5 or later. It was false on all of them. AccessTools.IsNetFrameworkRuntime is no longer true on .NET Core 3.x.
Match property access
CodeMatch.GetsProperty and CodeMatch.SetsProperty match calls to a property's getter or setter. They accept a PropertyInfo or a lambda such as () => default(MyClass).MyProperty. SymbolExtensions.GetPropertyInfo returns the property from such a lambda. See CodeMatcher.
Stack validation
Harmony rejects a patched method if the paths that reach an instruction have different stack depths. For runtimes that accept such IL, Harmony.ValidateStack and the environment variable HARMONY_VALIDATE_STACK turn this check off. See stack depth.
Earlier: Harmony 2
The following is the original overview of changes from 1.2.0.1 to Harmony 2, retained for reference.
New
- CI/CD with GitHub Actions
- Switched to
MonoMod.Corefor shared low-level patching with the MonoMod project - Works with more .NET versions
- Inline prevention for Mono
- 4th patch type:
Finalizer- for handling and manipulating exceptions - Reverse Patching (original onto one of your stub methods)
- Convenience extension methods for
CodeInstruction - Selective debug log with
[HarmonyDebug]annotation - works even with future changes of the method Prepare/Cleanupwill be called even with exceptions during patching- Cleanup can now receive and return the current Exception during patching
- Better exception reporting with
HarmonyException - Automatic documentation generated to
https://harmony.pardeike.net - AccessTools has methods for declared members
FastAccessnow deals with genericsManipulatortranspiler helper- Get IL code from a method
- Support for IL InlineSignature (patching methods with CALLI)
Fixed
- Priority field spelling
Traversecan handle static members- Methods returning struct types are now patchable
- Main API is now properly divided into static/instance methods
HarmonyMethodand other high-level APIs throw on null input- Patch sorting
- DeepCopy works with nullable types
- Patch annotations API cleaned up
FieldRefcovers more cases and is simplified__resultassignability checks- Handling
__statewithout Prefix - Debug log writes out full type names
- Documentation now uses compiled code snippets for correctness
Traverseworks with inherited fields, properties, and methods
Changes
- Removed Self-patching
- Renamed
Add()extension onIEnumerable<T>andT[]toAddItem()to avoid conflicts HarmonyInstanceis now calledHarmonyandHarmonynamespace is now calledHarmonyLib