Thanks to visit codestin.com
Credit goes to Github.com

Skip to content

MonoDetour/MonoDetour

Repository files navigation

MonoDetour

MonoDetour MonoDetour.HookGen Community Support
MonoDetour MonoDetour.HookGen Discord

Easy and convenient .NET detouring library based around HookGen with C# source generators, powered by MonoMod.RuntimeDetour.

Warning

This library is not fully released, and things will change.

  • Bugs and missing functionality is expected
  • Documentation may not reflect reality

Note: MonoDetour implements interop support for HarmonyX. This means that MonoDetour prefix and postfix hooks are aware of HarmonyX prefix and postfix hooks, and they respect each other.

Note

MonoDetour.HookGen's default HookGen namespace used to be On in versions < 0.7. Since >= 0.7, the HookGen namespace is Md (which stands for "MonoDetour").

This change was made due to an issue where if an API uses MonoDetour.HookGen with the On namespace, any consumers of that API who are also using MonoMod's own HookGen will get conflicts from a namespace with the same name as a type.

Documentation

Features

  • HookGen: Hooking any non-generic method is as easy as Md.Namespace.Type.Method.Prefix/Postfix/ILHook(MyHook);
    • Compiler generated unspeakable IEnumerator type instances are wrapped by a SpeakableEnumerator type, allowing easy access to standard fields; see Hooking IEnumerators
  • Prefix and Postfix hooks which throw will be caught and immediately disposed, including all of the owner MonoDetourManger's hooks
    • MonoDetourManger includes an event to gracefully disable your mod when any of its hooks throw
  • Extensible DetourType system: create your own e.g. RPCPrefixDetour for Unity NGO source generated RPC methods (TODO: HookGen integration)
    • MonoDetour hooks are just MonoMod ILHook wrappers which e.g. insert a call to your hook method
    • MonoDetour has 3 built-in detour types: PrefixDetour, PostfixDetour, and ILHookDetour
  • IL manipulation API ILWeaver which includes features such as matching against the "original" state of the target method's instructions as a fallback
    • Makes any matching statements much more robust without additional effort
  • Advanced CIL analysis in stack traces on ILHook manipulation target method throwing on compilation

Why

Full answer: https://monodetour.github.io/getting-started/why-monodetour/

TL;DR

MonoDetour hooks are similar to Harmony patches:

// Apply this hook somewhere
Md.Lib.TargetClass.TakeAndReturnInt.Prefix(Prefix_TakeAndReturnInt);
// ...
static void Prefix_TakeAndReturnInt(TargetClass self, ref int number)
{
    // ...
}

And here is HarmonyX:

[HarmonyPatch(typeof(TargetClass), nameof(TargetClass.TakeAndReturnInt))]
[HarmonyPrefix]
static void Prefix_TakeAndReturnInt(TargetClass __instance, ref int number)
{
    // ...
}

So why would you want to use this? Well, HarmonyX doesn't have HookGen, MonoMod HookGen v1 has issues (see full answer), and MonoMod HookGen v2 isn't finished and it doesn't generate the kind of hooks MonoDetour wants anyways. MonoMod HookGen v2 is great though, so MonoDetour's HookGen is based off of it.

You may be thinking that you don't need HookGen, and you are right. But it's so convenient when you use it that it hurts to go back. And new situations you might not have faced before such as hooking IEnumerator methods or methods with overloads is made easy thanks to HookGen! MonoDetour is a lot more than just HookGen though, even if that is the main selling point.

MonoDetour attempts to be the easiest and most convenient detouring library, and comes with great documentation (which is still a WIP)! MonoDetour has its own IL manipulation API called ILWeaver which (I promise) will offer the most extensive documentation of any IL manipulation APIs once it's finished.

Add to Your Project

Change the version number to optimally the newest:

<ItemGroup>
  <PackageReference Include="MonoDetour.HookGen" Version="0.7.*" PrivateAssets="all" />
  <PackageReference Include="MonoDetour" Version="[*,2.0)" /> <!-- Highest version below 2.0 -->
</ItemGroup>

MonoDetour.HookGen will automatically bring in the oldest MonoDetour reference it supports, so it's a good idea to specify the version for both.

Additionally MonoDetour automatically brings in the oldest MonoMod.RuntimeDetour version it supports, so also specify its version to the one you want (or don't if it's included by e.g. BepInEx references). MonoDetour should support MonoMod.RuntimeDetour versions 21.12.13.1 and 25.*, with possibly anything in between.

Note that MonoDetour.HookGen will strip unused generated hooks when building with Release configuration by default when MonoDetourHookGenStripUnusedHooks is undefined (evaluates to empty string).

You can configure this setting yourself however you wish, such as replicating the default behavior:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
  <MonoDetourHookGenStripUnusedHooks>partial</MonoDetourHookGenStripUnusedHooks>
</PropertyGroup>

Note

Possible values for MonoDetourHookGenStripUnusedHooks are:

  • false: no stripping
  • partial: unused hook helper types exist, but are empty
  • true: unused hook helper types aren't generated at all

Having this setting enabled will be more expensive generation wise, and when set to partial, intellisense may not keep up when writing hooks, e.g. Prefix, Postfix and ILHook methods may not immediately appear when typing Md.Namespace.Type.Method. even if they have just been generated. If the value is true, the hook generator may appear as if it was broken.

If the default HookGen namespace Md causes collisions or you just don't like it, you can set it with the following property:

<PropertyGroup>
  <MonoDetourHookGenNamespace>Md</MonoDetourHookGenNamespace>
</PropertyGroup>

Usage

MonoDetour is mainly designed to be used with HookGen. That is, MonoDetour generates helpers hooks to make hooking easy.

You can use the generated hooks like so:

[MonoDetourTargets(typeof(SomeType))]
class SomeTypeHooks
{
    [MonoDetourHookInitialize]
    static void InitHooks()
    {
        // Note: this is using the default generated MonoDetourManager
        // MonoDetour.HookGen.DefaultMonoDetourManager.Instance by default.
        // Use it for managing your hooks.
        Md.SomeNamespace.SomeType.SomeMethod.Prefix(Prefix_SomeType_SomeMethod);
    }

    static void Prefix_SomeType_SomeMethod(SomeType self)
    {
        Console.WriteLine("Hello from Prefix hook 1!");
    }
}

See https://monodetour.github.io/hooking/normal-methods/ for more information on hooking.

MonoDetour relies on ILHooks for hooking similar to HarmonyX. But instead of having monolithic ILHook methods like in HarmonyX, MonoDetour maps every hook to a unique ILHook.

Core Concepts

MonoDetourManager

https://monodetour.github.io/getting-started/monodetourmanager/

How Does the HookGen Work?

https://monodetour.github.io/getting-started/hookgen/

Credits

The HookGen source generator is heavily based on MonoMod.HookGen.V2. Without it, this project probably wouldn't exist.

About

Easy and convenient .NET detouring library, powered by MonoMod.RuntimeDetour.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages