Browse documentation

Install and apply patches

To use Harmony:

  1. find a way to run code inside the application (a loader or mod support)
  2. get 0Harmony.dll
  3. reference it from your project
  4. write patches in your code
  5. create a Harmony instance early in your code
  6. apply your patches
  7. compile and make sure Harmony is available at runtime

Runtime dependency

Some loaders or games already supply Harmony. Compile against APIs available in the version they load. Harmony supports multiple versions in one process, but the runtime's assembly-loading rules determine which DLL your code uses; bundling one does not guarantee it will be loaded.

Manual dll adding

In Visual Studio, right-click References, choose Add Reference, and browse to 0Harmony.dll.

Adding using nuget

In Visual Studio, right-click References, choose Manage NuGet Packages, and install Lib.Harmony.

Import

Import the namespace to use Harmony's API:

using HarmonyLib;

Creating a Harmony instance

Most patch operations need a Harmony instance:

var harmony = new Harmony("com.company.project.product");

Use a unique ID, preferably in reverse-domain notation. It identifies your patches and lets other authors order theirs before or after yours.

Debug Log

Set Harmony.DEBUG = true to log patching details and generated IL. FileLog writes to harmony.log.txt on your Desktop by default:

Harmony.DEBUG = true;

You can also use Harmony's file logger in your own code:

FileLog.Log("something");
// or buffered:
FileLog.LogBuffered("A");
FileLog.LogBuffered("B");
FileLog.FlushBuffer(); /* don't forget to flush */

Controlling FileLog with Environment Variables

Harmony reads these environment variables. Set them before starting the application:

Variable Effect
HARMONY_DEBUG Set to 1 or true to enable Harmony.DEBUG, logging patching details and generated IL. Set to false to disable it. Harmony reads this whenever a Harmony instance is created.
HARMONY_NO_LOG Any non-empty value disables the default file output, even when HARMONY_DEBUG is enabled. Unset it or leave it empty to allow file logging.
HARMONY_LOG_FILE Sets the log file path. If unset or empty, the default is harmony.log.txt on your Desktop. This does not enable debug logging by itself.

FileLog reads its settings once, when its log path is first accessed. A custom FileLog.LogWriter bypasses these file settings.

For example, enable debug logging and choose a log file in Bash or Zsh, then launch the application from the same shell:

export HARMONY_DEBUG=1
export HARMONY_LOG_FILE=/path/to/harmony.log.txt

Or enable debug logging when launching a game from Windows Command Prompt:

cmd /C "set HARMONY_DEBUG=1 && game.exe"

To disable the default file output instead:

export HARMONY_NO_LOG=1

Patching using annotations

Use PatchAll() to find and apply annotated patch classes in an assembly:

var assembly = Assembly.GetExecutingAssembly();
harmony.PatchAll(assembly);

// or implying current assembly:
harmony.PatchAll();

For groups applied at different times, mark classes with [HarmonyPatchCategory]. Use PatchCategory() for a selected group and PatchAllUncategorized() for classes without a category. PatchAll() ignores categories and applies them all.

Manual patching

For manual control, Patch() takes the original and optional prefix, postfix, transpiler, and finalizer methods, each wrapped in a HarmonyMethod:

// add null checks to the following lines, they are omitted for clarity
// when possible, don't use string and instead use nameof(...)
var original = typeof(TheClass).GetMethod("TheMethod");
var prefix = typeof(MyPatchClass1).GetMethod("SomeMethod");
var postfix = typeof(MyPatchClass2).GetMethod("SomeMethod");

harmony.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

// You can use named arguments to specify certain patch types only:
harmony.Patch(original, postfix: new HarmonyMethod(postfix));
harmony.Patch(original, prefix: new HarmonyMethod(prefix), transpiler: new HarmonyMethod(transpiler));

HarmonyMethod holds the patch method and settings such as priority. Manual and annotation patching share these settings.

var harmonyPostfix = new HarmonyMethod(postfix)
{
    priority = Priority.Low,
    before = ["that.other.harmony.user"]
};

Check that reflection found the original and patch methods: passing a missing (null) method causes an error. AccessTools can simplify these lookups.

Checking for existing patches

GetAllPatchedMethods() lists all patched methods in the current AppDomain:

var originalMethods = Harmony.GetAllPatchedMethods();
foreach (var method in originalMethods) { }

GetPatchedMethods() lists methods patched by your Harmony instance:

var myOriginalMethods = harmony.GetPatchedMethods();
foreach (var method in myOriginalMethods) { }

GetPatchInfo() describes everyone's patches on a method:

// get the MethodBase of the original
var original = typeof(TheClass).GetMethod("TheMethod");

// retrieve all patches
var patches = Harmony.GetPatchInfo(original);
if (patches is null) return; // not patched

// get a summary of all different Harmony ids involved
FileLog.Log("all owners: " + patches.Owners);

// get info about all Prefixes/Postfixes/Transpilers
foreach (var patch in patches.Prefixes)
{
    FileLog.Log("index: " + patch.index);
    FileLog.Log("owner: " + patch.owner);
    FileLog.Log("patch method: " + patch.PatchMethod);
    FileLog.Log("priority: " + patch.priority);
    FileLog.Log("before: " + patch.before);
    FileLog.Log("after: " + patch.after);
}

To detect another mod, look up one of its types by name. To check whether a Harmony ID has registered patches, use HasAnyPatches():

if (Harmony.HasAnyPatches("their.harmony.id")) { }

To see Harmony versions used by assemblies with active patches:

var dict = Harmony.VersionInfo(out var myVersion);
FileLog.Log("My version: " + myVersion);
foreach (var entry in dict)
{
    var id = entry.Key;
    var version = entry.Value;
    FileLog.Log("Mod " + id + " uses Harmony version " + version);
}

Unpatching

Unpatching removes selected registrations and rebuilds the method from its original IL and remaining patches. Removing every patch leaves a Harmony replacement with the original behavior.

You can remove all patches belonging to one Harmony ID, or everyone's patches:

// every patch on every method ever patched (including others patches):
var harmony = new Harmony("my.harmony.id");
harmony.UnpatchAll();

// only the patches that one specific Harmony instance did:
harmony.UnpatchAll("their.harmony.id");

Or remove specific patches:

var original = typeof(TheClass).GetMethod("TheMethod");

// all prefixes on the original method:
harmony.Unpatch(original, HarmonyPatchType.Prefix);

// all prefixes from that other Harmony user on the original method:
harmony.Unpatch(original, HarmonyPatchType.Prefix, "their.harmony.id");

// all patches from that other Harmony user:
harmony.Unpatch(original, HarmonyPatchType.All, "their.harmony.id");

// removing a specific patch:
var patch = typeof(TheClass).GetMethod("SomePrefix");
harmony.Unpatch(original, patch);
Harmony 3 preview

For the stable release, read the 2.x documentation.