### Access TryStart Property Source: https://monodetour.github.io/api/monodetour.cil.weaverexceptionfinallyhandler Gets or sets the inclusive start ILLabel for the Try block. ```csharp public ILLabel? TryStart { get; set; } ``` -------------------------------- ### Access HandlerStart Property Source: https://monodetour.github.io/api/monodetour.cil.weaverexceptionfinallyhandler Gets or sets the inclusive finally start ILLabel for the Finally block. ```csharp public ILLabel? HandlerStart { get; set; } ``` -------------------------------- ### Generated Hook Implementation Structure Source: https://monodetour.github.io/getting-started/hookgen Example of the internal partial class structure generated by HookGen for method hooking. ```csharp internal static partial class SomeMethod { public delegate void PrefixSignature(global::SomeType self); public delegate void PostfixSignature(global::SomeType self); public static global::MonoDetour.MonoDetourHook Prefix(PrefixSignature hook, global::MonoDetour.MonoDetourConfig? config = null, bool applyByDefault = true, global::MonoDetour.MonoDetourManager? manager = null) => (manager ?? global::MonoDetour.HookGen.DefaultMonoDetourManager.Instance) .Hook(Target(), hook.Method, config, applyByDefault); public static global::MonoDetour.MonoDetourHook Postfix(PostfixSignature hook, global::MonoDetour.MonoDetourConfig? config = null, bool applyByDefault = true, global::MonoDetour.MonoDetourManager? manager = null) => (manager ?? global::MonoDetour.HookGen.DefaultMonoDetourManager.Instance) .Hook(Target(), hook.Method, config, applyByDefault); public static global::MonoDetour.MonoDetourHook ILHook(global::MonoDetour.Cil.ILManipulationInfo.Manipulator manipulator, global::MonoDetour.MonoDetourConfig? config = null, bool applyByDefault = true, global::MonoDetour.MonoDetourManager? manager = null) => (manager ?? global::MonoDetour.HookGen.DefaultMonoDetourManager.Instance) .ILHook(Target(), manipulator, config, applyByDefault); public static global::System.Reflection.MethodBase Target() { var type = typeof(global::SomeType); var method = type.GetMethod("SomeMethod", (global::System.Reflection.BindingFlags)~0, null, [ ], null); if (method is null) throw new global::System.MissingMethodException("SomeType", "SomeMethod"); return method; } } ``` -------------------------------- ### Enumerator MoveNext State Machine Logic Source: https://monodetour.github.io/hooking/iterators Example of the internal state machine logic for a custom enumerator's MoveNext method. ```csharp private bool MoveNext() { switch (__1__state) { // Normally this would be for tracking the state, // but our IEnumerator method only has a single while loop. default: return false; case 0: __1__state = -1; break; case 1: __1__state = -1; break; } // Call the original enumerator's MoveNext method if (enumerator.MoveNext()) { // Set state and return with the result. __2__current = enumerator.Current; __1__state = 1; return true; } // If the original enumerator's MoveNext method // returned false, we also return false. return false; } ``` -------------------------------- ### Access TryEnd Property Source: https://monodetour.github.io/api/monodetour.cil.weaverexceptionfinallyhandler Gets or sets the inclusive try end ILLabel for the Try block. ```csharp public ILLabel? TryEnd { get; set; } ``` -------------------------------- ### Access HandlerEnd Property Source: https://monodetour.github.io/api/monodetour.cil.weaverexceptionfinallyhandler Gets or sets the inclusive finally end ILLabel for the Finally block. ```csharp public ILLabel? HandlerEnd { get; set; } ``` -------------------------------- ### Get or create SpeakableEnumerator Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Retrieves an existing SpeakableEnumerator or creates a new one for the provided enumerator instance. ```csharp public static SpeakableEnumerator GetOrCreate(IEnumerator instance) ``` -------------------------------- ### Access State property Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Gets or sets the state value of the enumerator, corresponding to the <>1__state field. ```csharp public int State { get; set; } ``` -------------------------------- ### Access Current property Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Gets or sets the current value of the enumerator, corresponding to the <>2__current field. ```csharp public TCurrent Current { get; set; } ``` -------------------------------- ### Define target method for ILHook Source: https://monodetour.github.io/ilhooking/basic-examples Example of a method containing a logic error that requires an ILHook to correct its conditional branching. ```csharp public static bool IsAboveZero(float num) { if (num >= 0) return true; else return false; } ``` -------------------------------- ### Invalid CIL Return Instruction Source: https://monodetour.github.io/ilhooking/introduction An example of an invalid CIL instruction sequence where an operand is incorrectly provided to the ret OpCode. ```CIL IL_0000: ret "Hello, World!" ``` -------------------------------- ### CreateInformationalSnapshotEvaluateAll Method Source: https://monodetour.github.io/api/monodetour.cil.analysis.methodbodyextensions Creates a snapshot that evaluates all instructions, including unreachable ones, useful for estimating stack sizes. ```csharp public static IInformationalMethodBody CreateInformationalSnapshotEvaluateAll(this MethodBody body) ``` -------------------------------- ### View target method IL instructions Source: https://monodetour.github.io/ilhooking/basic-examples The raw CIL instructions for the IsAboveZero method, showing the blt.un OpCode that needs replacement. ```text IL_0000: ldarg.0 // load arg (float)num IL_0001: ldc.r4 0.0 // load (float)0 IL_0006: blt.un.s IL_000a // branch to labelRetFalse if // (float)num is less than (float)0 IL_0008: ldc.i4.1 // load (int)1 (equivalent to 'true') IL_0009: ret // and return IL_000a: ldc.i4.0 // load (int)0 (equivalent to 'false') IL_000b: ret // and return ``` -------------------------------- ### CreateInformationalSnapshotJIT Method Source: https://monodetour.github.io/api/monodetour.cil.analysis.methodbodyextensions Creates a snapshot that evaluates instructions accurately to the JIT compiler, ignoring unreachable instructions. ```csharp public static IInformationalMethodBody CreateInformationalSnapshotJIT(this MethodBody body) ``` -------------------------------- ### CreateInformationalSnapshotEvaluateAll(MethodBody) Source: https://monodetour.github.io/api/monodetour.cil.analysis.methodbodyextensions Creates an IInformationalMethodBody snapshot of the provided MethodBody, evaluating all instructions including unreachable ones for stack size estimation. ```APIDOC ## CreateInformationalSnapshotEvaluateAll(MethodBody) ### Description Creates an IInformationalMethodBody snapshot of the provided MethodBody. This evaluates unreachable instructions which means this is not accurate for proper CIL analysis, but this can be useful when we want to estimate stack sizes of unreachable instructions. ### Signature `public static IInformationalMethodBody CreateInformationalSnapshotEvaluateAll(this MethodBody body)` ### Parameters - **body** (MethodBody) - The MethodBody to create an informational snapshot for. ### Returns - **IInformationalMethodBody** - An IInformationalMethodBody. ``` -------------------------------- ### CIL Stack Visualization Source: https://monodetour.github.io/ilhooking/introduction A sequence of CIL instructions demonstrating stack state changes using nop, ldstr, ldc.i4.0, and pop. ```CIL IL_0000: nop // stack: (empty) IL_0001: ldstr "Hello, World!" // stack: "Hello, World!" IL_0006: ldc.i4.0 // stack: "Hello, World!", 0 IL_0007: nop // stack: "Hello, World!", 0 IL_0008: pop // stack: "Hello, World!" IL_0009: ret // stack: (empty) ``` -------------------------------- ### Compare Instruction Matching with ILCursor and ILWeaver Source: https://monodetour.github.io/ilhooking/ilweaver-for-ilcursor-users Demonstrates the difference in syntax and approach between ILCursor's GotoNext and ILWeaver's MatchRelaxed for locating instructions. ```csharp static void ILHook_with_ILCursor(ILContext il) { ILCursor c = new(il); c.GotoNext( MoveType.After, x => x.MatchLdcI4(0), x => x.MatchCall(((Delegate)Stub).Method) ); c.Index--; c.Emit(OpCodes.Ldc_I4_1); c.Emit(OpCodes.Add); } ``` ```csharp static void ILHook_with_MonoDetour_and_ILWeaver(ILManipulationInfo info) { ILWeaver w = new(info); ILWeaverResult result = w.MatchRelaxed( x => x.MatchLdcI4(0), x => x.MatchCall(((Delegate)Stub).Method) && w.SetCurrentTo(x) ); // We can throw or check if match succeeded and get the error message if we want. result.ThrowIfFailure(); w.InsertBeforeCurrent( w.Create(OpCodes.Ldc_I4_1), w.Create(OpCodes.Add) ); } ``` -------------------------------- ### Managing MonoDetour Hooks Source: https://monodetour.github.io/getting-started/monodetourmanager Demonstrates the primary methods for initializing, applying, undoing, and disposing of hooks using the MonoDetourManager. ```csharp // Invoke hook initializers for this assembly // which are in types that are marked with [MonoDetourTargets] MonoDetourManager.InvokeHookInitializers(Assembly.GetExecutingAssembly()); // Applies all hooks belonging to this manager. // Note: By default, a MonoDetourManager won't have any hooks. // You need to initialize the hooks first, either calling them manually or using // MonoDetourManager.InvokeHookInitializers(Assembly) or any of its overloads. DefaultMonoDetourManager.Instance.ApplyHooks(); // Undoes all applied hooks belonging to this manager. DefaultMonoDetourManager.Instance.UndoHooks(); // Cleans up, undoes and gets rid of all hooks belonging to this manager. // Use this is you never want to see those hooks again. DefaultMonoDetourManager.Instance.DisposeHooks(); // Get the hook list from this MonoDetourManager. List hooks = DefaultMonoDetourManager.Instance.Hooks; ``` -------------------------------- ### Implement ILHook to replace instruction Source: https://monodetour.github.io/ilhooking/basic-examples Replaces the blt.un instruction with ble.un to correct the logic in IsAboveZero. ```csharp static void ILHook_IsAboveZero(ILManipulationInfo info) { ILWeaver w = new(info); // When matching the blt.un OpCode, we want to store the // branch label here for our replacement instruction. ILLabel? labelReturnFalse = null!; // We perform a sufficiently unique match on the instructions to // find the exact location we are looking for, and set the ILWeaver's // Current property to the instruction with the blt.un OpCode. ILWeaverResult result = w.MatchRelaxed( x => x.MatchLdarg(0), x => x.MatchLdcR4(0), x => x.MatchBltUn(out labelReturnFalse) && w.SetCurrentTo(x) ); // If the match failed, throw. result.ThrowIfFailure(); // We replace the instruction that has blt.un with our instruction. w.ReplaceCurrent(w.Create(OpCodes.Ble_Un, labelReturnFalse)); } ``` -------------------------------- ### Instruction Matching vs Location Assumption Source: https://monodetour.github.io/ilhooking/best-practices Demonstrates the difference between unsafe index-based replacement and safe instruction matching using ILWeaver. ```text IL_0000: ldstr "Hello, World!" IL_0005: ret ``` ```csharp static void ILHook_UnknownInstructions(ILManipulationInfo info) { ILWeaver w = new(info); // ❌ Do not assume target instructions' locations: w.ReplaceOperand(w.First, "ILHooked!"); // ✅ Do use instruction matching to find target instructions: ILWeaverResult result = w.MatchRelaxed( x => x.MatchLdstr("Hello, World!") && w.SetCurrentTo(x) ); result.ThrowIfFailure(); w.ReplaceCurrentOperand("ILHooked!"); } ``` -------------------------------- ### CreateInformationalSnapshotJIT(MethodBody) Source: https://monodetour.github.io/api/monodetour.cil.analysis.methodbodyextensions Creates an IInformationalMethodBody snapshot of the provided MethodBody, evaluating instructions as accurately as possible to the JIT compiler by ignoring unreachable instructions. ```APIDOC ## CreateInformationalSnapshotJIT(MethodBody) ### Description Creates an IInformationalMethodBody snapshot of the provided MethodBody. This will evaluate instructions as accurately as possible to the JIT compiler, which means that unreachable instructions aren’t evaluated. ### Signature `public static IInformationalMethodBody CreateInformationalSnapshotJIT(this MethodBody body)` ### Parameters - **body** (MethodBody) - The MethodBody to create an informational snapshot for. ### Returns - **IInformationalMethodBody** - An IInformationalMethodBody. ``` -------------------------------- ### Apply an ILHook to a target method Source: https://monodetour.github.io/ilhooking/introduction Demonstrates the syntax for registering an ILHook function to a specific target method. ```csharp Md.SomeNamespace.SomeType.ReturnHelloWorld.ILHook(ILHook_ReturnHelloWorld); ``` -------------------------------- ### Implement Runtime Detour Source: https://monodetour.github.io/ Use the MonoDetourTargets and MonoDetourHookInitialize attributes to define and apply a prefix hook to a target method. ```csharp using MonoDetour; using MonoDetour.HookGen; // Tell MonoDetour.HookGen to generate hook helpers for the target type [MonoDetourTargets(typeof(SomeClass))] static class SomeClassHooks { // Mark this method to be executed by MonoDetourManager.InvokeHookInitializers [MonoDetourHookInitialize] static void Init() { // Apply our prefix detour which runs before the target method. // 'Md' is the base namespace for generated hook helpers. Md.SomeApp.SomeClass.WriteMessage.Prefix(Prefix_SomeMethod) } static void Prefix_SomeMethod(SomeClass self, ref string message) { message = message.Replace("called", "hooked"); } } ``` -------------------------------- ### Add MonoDetour to Project Source: https://monodetour.github.io/getting-started/overview Include the required NuGet packages in your .csproj file to enable MonoDetour and its source generator. ```xml ``` -------------------------------- ### Safe Instruction Replacement Source: https://monodetour.github.io/ilhooking/best-practices Illustrates the correct way to replace instructions by creating modified copies rather than mutating existing instances. ```csharp static void ILHook_Replace_Example(ILManipulationInfo info) { ILWeaver w = new(info); ILWeaverResult result = w.MatchRelaxed( x => x.MatchCall(nameof(Foo.Bar)) && w.SetCurrentTo(x) ); result.ThrowIfFailure(); // ❌ Do not modify existing instruction instance: w.Current.Operand = myFooBarReplacementMethod; // ✅ Do replace the instruction with a copy that is modified: w.ReplaceCurrentOperand(myFooBarReplacementMethod); // ✅ Do replace the instruction: w.ReplaceCurrent( w.CreateDelegateCall(MyFooBarReplacement) ); } ``` -------------------------------- ### Initialize Hooks Source: https://monodetour.github.io/ Invoke the hook initializers within the application entry point to activate the defined detours. ```csharp using System.Reflection; using MonoDetour; // Call all methods marked with [MonoDetourHookInitialize] // in classes marked with [MonoDetourTargets] in this assembly MonoDetourManager.InvokeHookInitializers(Assembly.GetExecutingAssembly()); // Now when we call WriteMessage, // our prefix detour will run before the original method runs SomeClass someClass = new(); someClass.WriteMessage("I have been called!"); // Prints: // I have been hooked! ``` -------------------------------- ### Initialize MonoDetourManager Source: https://monodetour.github.io/api/monodetour.monodetourmanager Constructor for creating a new manager instance with a unique identifier. ```csharp public MonoDetourManager(string id) ``` -------------------------------- ### Basic CIL Method Return Source: https://monodetour.github.io/ilhooking/introduction A simple CIL method that loads a string onto the stack and returns it. ```CIL IL_0000: ldstr "Hello, World!" IL_0005: ret ``` -------------------------------- ### Implement Basic Prefix Hook Source: https://monodetour.github.io/hooking/normal-methods Define a prefix hook using the MonoDetourHookInitialize attribute to modify parameters or execute logic before the target method. ```csharp [MonoDetourTargets(typeof(LibraryClass))] static class LibraryClassHooks { // MonoDetourManager.InvokeHookInitializers will // call methods marked with this attribute in types // which have the MonoDetourTargetsAttribute. [MonoDetourHookInitialize] static void Init() { // Add a prefix hook which runs at the start of the target method. Md.Lib.LibraryClass.TakeAndReturnInt.Prefix(Prefix_TakeAndReturnInt); } static void Prefix_TakeAndReturnInt(LibraryClass self, ref int number) { // As soon as LibraryClass.TakeAndReturnInt runs, // call its method PrintFoo with its instance. self.PrintFoo(); // Increment the 'number' parameter by one. number += 1; } } ``` -------------------------------- ### Initialize All Hooks Source: https://monodetour.github.io/hooking/normal-methods Use MonoDetourManager to automatically invoke all methods marked with the hook initialization attribute. ```csharp internal static void InitAllHooks() { // Searches for types with MonoDetourTargets to go through and // finds methods in them marked with MonoDetourHookInitialize and invokes them. MonoDetourManager.InvokeHookInitializers(Assembly.GetExecutingAssembly()); } ``` -------------------------------- ### Implement Postfix Hook Source: https://monodetour.github.io/hooking/normal-methods Define a postfix hook to execute logic after the target method completes, with access to the return value. ```csharp // Add a postfix hook which runs at the end of the target method. Md.Lib.LibraryClass.TakeAndReturnInt.Postfix(Postfix_TakeAndReturnInt); // ... static void Postfix_TakeAndReturnInt(LibraryClass self, ref int number, ref int returnValue) { Console.WriteLine("Hello from postfix hook!"); } ``` -------------------------------- ### Implement ILHook Source: https://monodetour.github.io/ilhooking/basic-examples Initialization and implementation of the ILHook to modify the target method. ```csharp // Somewhere, initialize the ILHook Md.Gun.OnUse.ILHook(ILHook_OnUse); ``` ```csharp static void ILHook_OnUse(ILManipulationInfo info) { ILWeaver w = new(info); // As always, we perform a match and point Current to what we are looking for. ILWeaverResult result = w.MatchRelaxed( x => x.MatchLdloc(out _), x => x.MatchLdcI4(out _), x => x.MatchCallvirt(nameof(Entity.TakeDamage)) && w.SetCurrentTo(x) ); result.ThrowIfFailure(); // We insert our instructions just before the Entity.TakeDamage method call. w.InsertBeforeCurrent( // Load arg 0, which is always 'this' for instance (non-static) methods w.Create(OpCodes.Ldarg_0), // Add a call to our method which consumes the base damage and 'this' from the stack. w.CreateDelegateCall(CalculateDamage) ); // CalculateDamage returns int, so stack becomes [Entity, int] for // the Entity.TakeDamage method, which is correct. } static int CalculateDamage(int baseDamage, Gun self) { if (self._ammo == 0) return baseDamage * 2; else return baseDamage; } ``` -------------------------------- ### Apply ILHook via MonoDetourManager Source: https://monodetour.github.io/ilhooking/basic-examples Boilerplate for initializing hooks and registering them with the MonoDetourManager. ```csharp [MonoDetourTargets(typeof(SomeApp))] static class SomeAppHooks { [MonoDetourHookInitialize] static void Init() { Md.SomeNamespace.SomeApp.IsAboveZero.ILHook(ILHook_IsAboveZero); } static void ILHook_IsAboveZero(ILManipulationInfo info) { // ... code here ... } } ``` ```csharp MonoDetourManager.InvokeHookInitializers(Assembly.GetExecutingAssembly()); ``` -------------------------------- ### InvokeHookInitializers Source: https://monodetour.github.io/api/monodetour.monodetourmanager Static methods to invoke hook initializers for a given assembly or type. ```APIDOC ## InvokeHookInitializers(Assembly, bool) / InvokeHookInitializers(Type, bool) ### Description Invokes hook initializers for the specified assembly or type. ### Parameters - **assembly/type** (Assembly/Type) - Required - The target to invoke initializers for. - **reportUnloadableTypes** (bool) - Optional - Whether to log on unloadable types (default: true). ``` -------------------------------- ### ILHook(MethodBase, Manipulator, MonoDetourConfig?, bool) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates a ILHookDetour hook using the provided MethodBase and manipulator. ```APIDOC ## ILHook(MethodBase, Manipulator, MonoDetourConfig?, bool) ### Description Creates a ILHookDetour hook using the information defined. This method is intended for use by HookGen. ### Parameters - **target** (MethodBase) - The method to be hooked. - **manipulator** (ILManipulationInfo.Manipulator) - The manipulator method. - **config** (MonoDetourConfig) - The priority configuration for this hook. - **applyByDefault** (bool) - Whether or not the hook should be applied after it has been constructed. ### Returns - **MonoDetourHook** - The hook. ``` -------------------------------- ### Pre-build field reference getters Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Pre-builds field reference getters for a specific type to avoid JIT-related performance freezes. ```csharp public static void PreBuildFieldReferenceGetters(Type type) ``` -------------------------------- ### Hook(MethodBase, MethodBase, MonoDetourConfig?, bool) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates a MonoDetour Hook using the provided MethodBase objects. ```APIDOC ## Hook(MethodBase, MethodBase, MonoDetourConfig?, bool) ### Description Creates a MonoDetour Hook using the information defined. This method is intended for use by HookGen. ### Parameters - **target** (MethodBase) - The method to be hooked. - **manipulator** (MethodBase) - The hook or manipulator method. - **config** (MonoDetourConfig) - Metadata configuration for the MonoDetour Hook. - **applyByDefault** (bool) - Whether or not the hook should be applied after it has been constructed. ### Returns - **MonoDetourHook** - The hook. ``` -------------------------------- ### MonoDetourManager(string) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Initializes a new instance of the MonoDetourManager class with a specified identifier. ```APIDOC ## MonoDetourManager(string) ### Description Initializes a new instance of the MonoDetourManager class. ### Parameters - **id** (string) - Required - The identifier for this manager, used in MonoDetourConfig. ``` -------------------------------- ### Hook(MethodBase, Delegate, MonoDetourConfig?, bool) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates a MonoDetour Hook using the provided MethodBase and delegate. ```APIDOC ## Hook(MethodBase, Delegate, MonoDetourConfig?, bool) ### Description Creates a MonoDetour Hook using the information defined. This method is intended for use by HookGen. ### Parameters - **target** (MethodBase) - The method to be hooked. - **manipulator** (Delegate) - The hook or manipulator method. - **config** (MonoDetourConfig) - Metadata configuration for the MonoDetour Hook. - **applyByDefault** (bool) - Whether or not the hook should be applied after it has been constructed. ### Returns - **MonoDetourHook** - The hook. ``` -------------------------------- ### ILHook(Delegate, Manipulator, MonoDetourConfig?, bool) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates a ILHookDetour hook using the provided delegate and manipulator. ```APIDOC ## ILHook(Delegate, Manipulator, MonoDetourConfig?, bool) ### Description Creates a ILHookDetour hook using the information defined. This method is intended for use by HookGen. ### Parameters - **target** (Delegate) - The method to be hooked. - **manipulator** (ILManipulationInfo.Manipulator) - The manipulator method. - **config** (MonoDetourConfig) - The priority configuration for this hook. - **applyByDefault** (bool) - Whether or not the hook should be applied after it has been constructed. ### Returns - **MonoDetourHook** - The hook. ``` -------------------------------- ### Implement a hook using MonoDetour Source: https://monodetour.github.io/getting-started/why-monodetour Uses the MonoDetour attribute-based system to define a prefix hook for the target method. ```csharp // Generate hooks for the target class. [MonoDetourTargets(typeof(TargetClass))] static class TargetClassHook { // This attribute is not required, but will use this as hook init method. [MonoDetourHookInitialize] static void Init() { // These hooks are generated, so they are quick to write // and nearly impossible to get wrong! Md.Lib.TargetClass.TakeAndReturnInt.Prefix(Prefix_TakeAndReturnInt); } // This method signature can be generated by your IDE from the PrefixSignature // delegate argument in the Md.Lib.TargetClass.TakeAndReturnInt.Prefix method! private static void Prefix_TakeAndReturnInt(TargetClass self, ref int number) { self.PrintFoo(); number += 1; } } ``` -------------------------------- ### Create ILHook with Delegate or MethodBase Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates an ILHookDetour using either a Delegate or MethodBase target. These methods are intended for use by HookGen rather than direct invocation. ```csharp public MonoDetourHook ILHook(Delegate target, ILManipulationInfo.Manipulator manipulator, MonoDetourConfig? config = null, bool applyByDefault = true) ``` ```csharp public MonoDetourHook ILHook(MethodBase target, ILManipulationInfo.Manipulator manipulator, MonoDetourConfig? config = null, bool applyByDefault = true) ``` -------------------------------- ### Match Repeating Pattern with ILCursor Source: https://monodetour.github.io/ilhooking/ilweaver-for-ilcursor-users Uses a while loop with TryGotoNext to repeatedly find and replace instructions. ```csharp static void ILHook_with_ILCursor(ILContext il) { ILCursor c = new(il); while ( c.TryGotoNext( MoveType.Before, x => x.MatchCall(nameof(Assembly.GetTypes)) ) ) { c.Remove(); c.MoveAfterLabels(); c.EmitDelegate(GetTypesSafe); } // If you consider no matches a fail, we could throw or log here with additional logic } ``` -------------------------------- ### Safe Instruction Insertion Source: https://monodetour.github.io/ilhooking/best-practices Shows how to avoid breaking method compilation by cloning instructions instead of inserting duplicates. ```csharp static void ILHook_Insert_Example(ILManipulationInfo info) { ILWeaver w = new(info); ILWeaverResult result = w.MatchRelaxed( x => x.MatchLdsfld(nameof(Foo.Baz)) && w.SetCurrentTo(x), x => x.MatchCall(nameof(Foo.Bar)) ); result.ThrowIfFailure(); // ❌ Do not insert duplicate instruction instance: w.InsertBeforeCurrent( w.Current, w.CreateDelegateCall(MyMethod) ); // ✅ Do insert a copy of the instruction: w.InsertBeforeCurrent( w.Current.Clone(), w.CreateDelegateCall(MyMethod) ); } ``` -------------------------------- ### Prefixing the MoveNext method Source: https://monodetour.github.io/hooking/iterators Executes logic at the beginning of every MoveNext call. ```csharp Md.Namespace.ContainingClass.GetMessages.PrefixMoveNext(Prefix_GetMessages_MoveNext); // ... static void Prefix_GetMessages_MoveNext(SpeakableEnumerator self) { // This runs at the beginning of every MoveNext call } ``` -------------------------------- ### Manually Enumerate IEnumerator Source: https://monodetour.github.io/hooking/iterators Demonstrates how to manually iterate through an IEnumerator when a foreach loop cannot be used. ```csharp IEnumerator enumerator = ContainingType.GetMessages(); while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); } // Prints: // Hello from 1st MoveNext! // Hello from 2nd MoveNext! ``` -------------------------------- ### Hook(Delegate, Delegate, MonoDetourConfig?, bool) Source: https://monodetour.github.io/api/monodetour.monodetourmanager Creates a MonoDetour Hook using the provided delegates. ```APIDOC ## Hook(Delegate, Delegate, MonoDetourConfig?, bool) ### Description Creates a MonoDetour Hook using the information defined. This method is intended for use by HookGen. ### Parameters - **target** (Delegate) - The method to be hooked. - **manipulator** (Delegate) - The hook or manipulator method. - **config** (MonoDetourConfig) - Metadata configuration for the MonoDetour Hook. - **applyByDefault** (bool) - Whether or not the hook should be applied after it has been constructed. ### Returns - **MonoDetourHook** - The hook. ``` -------------------------------- ### Rewrite logic for IsAboveZero Source: https://monodetour.github.io/ilhooking/basic-examples C# representation of the target method's control flow to identify the necessary logic change. ```csharp public static bool IsAboveZero(float num) { if (num < 0) goto labelReturnFalse; return true; labelReturnFalse: return false; } ``` -------------------------------- ### Define Target Class and Method Source: https://monodetour.github.io/ilhooking/basic-examples The original class structure containing the method to be hooked. ```csharp class Gun(int magazineSize) : Item { internal int _ammo = magazineSize; public override void OnUse(Entity user) { if (_ammo <= 0) return; _ammo--; if (!user.TryGetTarget(out Entity? target)) { return; } target.TakeDamage(4); } } ``` -------------------------------- ### Manage hook lifecycle Source: https://monodetour.github.io/api/monodetour.monodetourmanager Methods to apply, undo, or dispose of hooks managed by the instance. ```csharp public void ApplyHooks() ``` ```csharp public void UndoHooks() ``` ```csharp public void DisposeHooks() ``` -------------------------------- ### HookTargetRecords.TargetPrefixInfo Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords.targetprefixinfo The TargetPrefixInfo class stores metadata and state regarding prefix hook control flow. ```APIDOC ## Class: HookTargetRecords.TargetPrefixInfo ### Properties - **ControlFlow** (VariableDefinition) - The local int used for determining the control flow of the method. - **TemporaryControlFlow** (VariableDefinition) - The temporary local int used for setting the ControlFlow int. - **ControlFlowImplemented** (bool) - Whether or not Prefix control flow has been implemented in the method. ### Methods #### SetControlFlowImplemented() Sets the ControlFlowImplemented property to true. ``` -------------------------------- ### Implement hooks using MonoDetour attributes Source: https://monodetour.github.io/getting-started/introduction Use the MonoDetourTargets attribute to define a hook class and MonoDetourHookInitialize to register hook methods. The manager handles the invocation of these initializers. ```csharp // Generate helper hooks for the target type. [MonoDetourTargets(typeof(SomeType))] static class SomeTypeHooks { // MonoDetourManager.InvokeHookInitializers will // call methods marked with this attribute in types // which have the MonoDetourTargetsAttribute. [MonoDetourHookInitialize] internal 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_SomeMethod); } private static void Prefix_SomeMethod(SomeType self, ref int number) { Console.WriteLine("Hello from Prefix hook!"); } } ``` -------------------------------- ### Invoke hook initializers for assemblies Source: https://monodetour.github.io/api/monodetour.monodetourmanager Static methods to trigger hook initializers within a specified assembly, with an optional flag to report unloadable types. ```csharp public static void InvokeHookInitializers(Assembly assembly, bool reportUnloadableTypes = true) ``` ```csharp public static void InvokeHookInitializers(Assembly assembly) ``` -------------------------------- ### Invoke hook initializers for types Source: https://monodetour.github.io/api/monodetour.monodetourmanager Static methods to trigger hook initializers within a specified type, with an optional flag to report unloadable types. ```csharp public static void InvokeHookInitializers(Type type, bool reportUnloadableTypes = true) ``` ```csharp public static void InvokeHookInitializers(Type type) ``` -------------------------------- ### Initialize SpeakableEnumerator Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Constructor for creating a new instance of SpeakableEnumerator wrapping an existing enumerator. ```csharp public SpeakableEnumerator(IEnumerator instance) ``` -------------------------------- ### Postfixing the MoveNext method Source: https://monodetour.github.io/hooking/iterators Executes logic at the end of every MoveNext call, with access to the enumeration state. ```csharp Md.Namespace.ContainingClass.GetMessages.PostfixMoveNext(Postfix_GetMessages_MoveNext); // ... static void Postfix_GetMessages_MoveNext( SpeakableEnumerator self, ref bool continueEnumeration ) { // This runs at the end of every MoveNext call } ``` -------------------------------- ### Inspect IL Instructions Source: https://monodetour.github.io/ilhooking/basic-examples The IL code of the target method used to identify the injection point. ```il IL_0000: ldarg.0 IL_0001: ldfld int32 Gun::_ammo IL_0006: ldc.i4.0 IL_0007: bgt.s IL_000a IL_0009: ret IL_000a: ldarg.0 IL_000b: ldarg.0 IL_000c: ldfld int32 Gun::_ammo IL_0011: ldc.i4.1 IL_0012: sub IL_0013: stfld int32 Gun::_ammo IL_0018: ldarg.1 IL_0019: ldloca.s 0 IL_001b: callvirt instance bool Entity::TryGetTarget(class Entity&) IL_0020: brtrue.s IL_0023 IL_0022: ret IL_0023: ldloc.0 IL_0024: ldc.i4.4 IL_0025: callvirt instance void Entity::TakeDamage(int32) // <-- We want here IL_002a: ret ``` -------------------------------- ### Wrap an Enumerator via Postfix Source: https://monodetour.github.io/hooking/iterators Demonstrates how to intercept an enumerator returned by a method and wrap it with custom logic. ```csharp public static class ContainingClass { public static IEnumerator GetMessages() { IEnumerator originalEnumerator = new _GetMessages_d__0(0); // Insert our postfix here: IEnumerator ourEnumerator = MyHooks.Wrapper_GetMessages(originalEnumerator); return ourEnumerator; } // ... } ``` ```csharp // The following would be in your code: [MonoDetourTargets(typeof(ContainingClass))] static class MyHooks { [MonoDetourHookInitialize] static void Init() { // This hook inserts a call to our wrapper method like in the above demonstration. Md.Namespace.ContainingClass.GetMessages.Postfix(Postfix_GetMessages); } // This postfix hook takes the original enumerator and // returns its own enumerator object which wraps the original. static void Postfix_GetMessages(ref IEnumerator returnValue) { returnValue = Wrapper_GetMessages(returnValue); } static IEnumerator Wrapper_GetMessages(IEnumerator original) { // Code here runs only at the beginning of enumeration... // Enumerate the whole original enumerator object to completion while (original.MoveNext()) { // On each MoveNext call of the original, we return its return value. // Or, we could also return a different value if we wanted to. yield return original.Current; } // Code here runs only at the end of enumeration... } } ``` -------------------------------- ### HookTargetRecords.TargetPrefixInfo Class Definition Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords.targetprefixinfo Represents the structure for prefix hook information. ```csharp public class HookTargetRecords.TargetPrefixInfo ``` -------------------------------- ### Implement IMonoDetourHookApplier for custom detours Source: https://monodetour.github.io/getting-started/detour-types Implement this interface to create custom detour types. The implementing class must have a parameterless constructor. ```csharp /// /// A type which implements this interface can be used as a /// detour type, meaning the type can be passed in as a generic parameter /// to construction methods such as /// .
///
/// MonoDetour uses this to implement , /// and . /// If none of the available detour types satisfy your needs, /// you can implement your own. See any of the implemented detour types for reference. ///
/// /// For MonoDetour to be able to use a type which implements this /// interface, the type must have a parameterless constructor. /// public interface IMonoDetourHookApplier { /// /// All the available metadata for the MonoDetour Hook. /// IReadOnlyMonoDetourHook Hook { get; set; } /// /// The method that is called /// when the ILHook is applied. /// /// The passed for manipulating the target method. void ApplierManipulator(ILContext il); } ``` -------------------------------- ### Implement ControlFlowPrefix Hook Source: https://monodetour.github.io/hooking/normal-methods Define a ControlFlowPrefix method to dynamically alter return values and execution flow based on the ReturnFlow enum. ```csharp // 1. MUST SET: GenerateControlFlowVariants = true [MonoDetourTargets(typeof(LibraryClass), GenerateControlFlowVariants = true)] static class LibraryClassHooks { [MonoDetourHookInitialize] static void Init() { // 2. Now ControlFlowPrefix hooks can be used and applied Md.Lib.LibraryClass.TakeAndReturnInt.ControlFlowPrefix( ControlFlowPrefix_TakeAndReturnInt ); } static ReturnFlow ControlFlowPrefix_TakeAndReturnInt( LibraryClass self, ref int number, ref int returnValue ) { // We can set the to-be return value of the // target method by assigning to returnValue: returnValue = 1337; // We can dynamically choose the return behavior // of our ControlFlowPrefix hook. return number switch { 0 => ReturnFlow.None, 1 => ReturnFlow.SkipOriginal, _ => ReturnFlow.HardReturn, }; } } ``` -------------------------------- ### ApplyHooks Source: https://monodetour.github.io/api/monodetour.monodetourmanager Applies all hooks currently registered to the manager instance. ```APIDOC ## ApplyHooks() ### Description Applies all hooks belonging to this manager. Hooks must be initialized prior to calling this method. ``` -------------------------------- ### Match IL instructions using ILPatternMatchingExt Source: https://monodetour.github.io/ilhooking/basic-examples Use these lambda expressions with ILPatternMatchingExt to identify specific OpCodes and operands within a method's instruction stream. ```csharp // Match Ldarg with Operand 0 x => x.MatchLdarg(0), // Match Ldfld with any Operand and discard 'out' Operand value x => x.MatchLdfld(out _), // Match Brtrue and store Operand value to a local variable x => x.MatchBrtrue(out branchTarget), // Match Call with any method starting with '' x => x.MatchCall(out var method) && method.Name.StartsWith("", StringComparison.Ordinal), ``` -------------------------------- ### Dispose() Source: https://monodetour.github.io/api/monodetour.monodetourmanager Releases unmanaged resources. ```APIDOC ## Dispose() ### Description Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. ``` -------------------------------- ### GetHookTargetInfo via MethodDefinition Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords Retrieves hook target information using a MethodDefinition instance. ```csharp public static HookTargetRecords.HookTargetInfo GetHookTargetInfo(MethodDefinition method) ``` -------------------------------- ### Enable Control Flow Variants Source: https://monodetour.github.io/hooking/normal-methods Apply the MonoDetourTargets attribute with GenerateControlFlowVariants set to true to enable control flow hooks. ```csharp [MonoDetourTargets(typeof(LibraryClass), GenerateControlFlowVariants = true)] static class LibraryClassHooks { } ``` -------------------------------- ### Hooking Property Getters and Setters Source: https://monodetour.github.io/hooking/normal-methods Target property accessors by using the 'get_' or 'set_' prefix. Ensure the property implements a setter before attempting to hook it. ```csharp // Hook the getter method: Md.Lib.LibraryClass.get_IsTrue.Prefix(Prefix_get_IsTrue); // Hook the setter method: Md.Lib.LibraryClass.set_IsTrue.Prefix(Prefix_set_IsTrue); // Note that if the property doesn't implement // a setter, there won't be a hook for it. ``` -------------------------------- ### ControlFlowImplemented Property Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords.targetprefixinfo Indicates whether prefix control flow has been implemented in the target method. ```csharp public bool ControlFlowImplemented { get; } ``` -------------------------------- ### PreBuildFieldReferenceGetters(Type) Source: https://monodetour.github.io/api/monodetour.reflection.unspeakable.speakableenumerator-1 Pre-builds field reference getters for a specific type to avoid JIT-related performance issues. ```APIDOC ## Method: PreBuildFieldReferenceGetters(Type type) ### Description Can be used for building field reference getter methods ahead-of-time to prevent a freeze that would happen when building them just-in-time. ### Parameters - **type** (Type) - Required - The type of Enumerator to build field reference getters for. ### Exceptions - **NullReferenceException** - **InvalidCastException** ``` -------------------------------- ### GetHookTargetInfo(MethodDefinition) Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords Retrieves a HookTargetRecords.HookTargetInfo object for a target method using the provided MethodDefinition. ```APIDOC ## GetHookTargetInfo(MethodDefinition) ### Description Gets a HookTargetRecords.HookTargetInfo for a target method using the provided MethodDefinition. ### Signature `public static HookTargetRecords.HookTargetInfo GetHookTargetInfo(MethodDefinition method)` ### Parameters - **method** (MethodDefinition) - Required - The target MethodDefinition. ### Returns - **HookTargetRecords.HookTargetInfo** - A HookTargetRecords.HookTargetInfo for the target method. ``` -------------------------------- ### Implement a hook using Harmony Source: https://monodetour.github.io/getting-started/why-monodetour Uses Harmony attributes to patch the target method, requiring specific parameter naming conventions. ```csharp // Tell Harmony to use this targe type in patch methods inside this class. [HarmonyPatch(typeof(TargetClass))] static class TargetClassPatch { // Specify target method name to patch. // Note: methods with overloads require specifying argument types to // identify the method to hook. [HarmonyPatch(nameof(TargetClass.TakeAndReturnInt))] // Mark patch as a Prefix. Alternatively the method name could be 'Prefix'. [HarmonyPrefix] // Use special argument names to get parameters. // Note: Your IDE can't help you with your patch signature here like with HookGen! static void Prefix_TakeAndReturnInt(TargetClass __instance, ref int number) { __instance.PrintFoo(); number += 1; } } ``` -------------------------------- ### ILHooking the MoveNext method Source: https://monodetour.github.io/hooking/iterators Manipulates the IL of the MoveNext method directly. ```csharp Md.Namespace.ContainingClass.GetMessages.ILHookMoveNext(ILHook_GetMessages_MoveNext); // ... static void ILHook_GetMessages_MoveNext(ILManipulationInfo info) { // Manipulate the MoveNext method here, // for example change the strings that are returned. } ``` -------------------------------- ### ILManipulationInfo.Manipulator Delegate Source: https://monodetour.github.io/api/monodetour.cil.ilmanipulationinfo.manipulator Defines the signature for an IL manipulator method that accepts an ILManipulationInfo object. ```APIDOC ## Delegate: ILManipulationInfo.Manipulator ### Definition `public delegate void ILManipulationInfo.Manipulator(ILManipulationInfo info)` ### Description An IL manipulator method accepting an ILManipulationInfo object to perform modifications on the IL context. ### Parameters - **info** (ILManipulationInfo) - Required - A manipulation info containing the MonoMod.Cil.ILContext intended for manipulation and an untouched MonoMod.Cil.ILContext for observing the original state. ``` -------------------------------- ### Define Target Method Source: https://monodetour.github.io/ The target class and method to be modified at runtime. ```csharp namespace SomeApp; public class SomeClass { public void WriteMessage(string message) => Console.WriteLine(message); } ``` -------------------------------- ### HandlerPart Field Definitions Source: https://monodetour.github.io/api/monodetour.cil.analysis.iinformationalinstruction.handlerpart Individual field definitions for the HandlerPart enumeration. ```csharp None = 0 ``` ```csharp TryStart = 1 ``` ```csharp TryEnd = 2 ``` ```csharp FilterStart = 4 ``` ```csharp HandlerStart = 8 ``` ```csharp HandlerEnd = 16 ``` ```csharp BeforeTryStart = 32 ``` ```csharp TryOrHandlerEnd = TryEnd | HandlerEnd ``` ```csharp FilterOrHandlerStart = FilterStart | HandlerStart ``` -------------------------------- ### GetHookTargetInfo(ILContext) Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords Retrieves a HookTargetRecords.HookTargetInfo object for a target method using the provided ILContext. ```APIDOC ## GetHookTargetInfo(ILContext) ### Description Gets a HookTargetRecords.HookTargetInfo for a target method using the provided ILContext. ### Signature `public static HookTargetRecords.HookTargetInfo GetHookTargetInfo(ILContext il)` ### Parameters - **il** (MonoMod.Cil.ILContext) - Required - The MonoMod.Cil.ILContext for the target method. ### Returns - **HookTargetRecords.HookTargetInfo** - A HookTargetRecords.HookTargetInfo for the target method. ``` -------------------------------- ### GetHookTargetInfo via ILContext Source: https://monodetour.github.io/api/monodetour.detourtypes.manipulation.hooktargetrecords Retrieves hook target information using an ILContext instance. ```csharp public static HookTargetRecords.HookTargetInfo GetHookTargetInfo(ILContext il) ``` -------------------------------- ### Implement a hook using MonoMod Source: https://monodetour.github.io/getting-started/why-monodetour Uses the standard MonoMod Hook class to manually intercept the method call. ```csharp static class TargetClassHook { // Store the hook instance. static Hook hookTakeAndReturnInt; internal static void Init() { // Specify target method name to patch. // Note: methods with overloads require specifying argument types to // identify the method to hook. hookTakeAndReturnInt = new Hook( typeof(TargetClass).GetMethod(nameof(TargetClass.TakeAndReturnInt)), Hook_TakeAndReturnInt ); } // Specify the original method signature as the first argument, and also // separately as individual parameters including the return type for your hook. // Note: Your IDE can't help you with your patch signature here like with HookGen! static int Hook_TakeAndReturnInt( Func orig, TargetClass self, int number) { self.PrintFoo(); return orig(self, number + 1); } } ``` -------------------------------- ### Match Repeating Pattern with ILWeaver Source: https://monodetour.github.io/ilhooking/ilweaver-for-ilcursor-users Uses MatchMultipleRelaxed to safely identify and process all matches without manual loop management. ```csharp static void ILHook_with_MonoDetour_and_ILWeaver(ILManipulationInfo info) { ILWeaver w = new(info); ILWeaverResult result = w.MatchMultipleRelaxed( // First argument is a callback for every successful match ILWeaver match => { match.ReplaceCurrent(w.CreateDelegateCall(GetTypesSafe)); }, x => x.MatchCall(nameof(Assembly.GetTypes)) && w.SetCurrentTo(x) ); // If you consider no matches a fail, we could throw or log here } ``` -------------------------------- ### ReturnFlow HardReturn Field Source: https://monodetour.github.io/api/monodetour.detourtypes.returnflow Returns immediately without executing any further hooks attached to the method. ```csharp HardReturn = 2 ```