pardeike / Harmony

A library for patching, replacing and decorating .NET and Mono methods during runtime
https://www.patreon.com/pardeike
MIT License
5.26k stars 492 forks source link

cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) #227

Closed CannibarRechter closed 4 years ago

CannibarRechter commented 4 years ago

Describe the bug

Exception issued when attempting to patch an inner class, as follows:

ConduitFlow+ConduitContents.GetEffectiveCapacity(System.Single) cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) ConduitFlow/ConduitContents:GetEffectiveCapacity_Patch1 (object,single): IL_000d: call 0x00000001 at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in :0 at Harmony.PatchProcessor.Patch () [0x00000] in :0 at Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyMethod transpiler) [0x00000] in :0 at HeatExchange.HeatExchangerPatches+Mod_OnLoad.OnLoad () [0x00000] in :0

harmony.log.txt:

Patch ConduitFlow+ConduitContents, Single GetEffectiveCapacity(Single)

L_0000: Local var 0: System.Single L_0000: Local var 1: System.Single L_0000: ldc.r4 0 L_0005: stloc 1 (System.Single) L_0006: ldarg.0 L_0007: ldarg 1 L_000d: call Void Prefix(ConduitContents, Single) L_0012: ldarg.0 L_0013: call Single get_mass() L_0018: stloc.0 L_0019: ldloc.0 L_001a: ldarg.1 L_001b: cgt.un L_001d: ldc.i4.0 L_001e: ceq L_0020: ldstr "Effective mass cannot be greater than capacity!" L_0025: call Void DevAssert(Boolean, System.String) L_002a: ldc.r4 0 L_002f: ldarg.1 L_0030: ldloc.0 L_0031: sub L_0032: call Single Max(Single, Single) L_0037: stloc 1 (System.Single) L_0038: ldloc 1 (System.Single) L_0039: ret DONE

To Reproduce

Occurs with a prefix or postfix patch. Occurs with an annotation, or with an explicit call. Here is the outer class header:

public class ConduitFlow : IConduitFlow {

Here is the inner (struct) error:

    public struct ConduitContents
{

Here it the method defintiion:

    public float GetEffectiveCapacity(float maximum_capacity)
    {
        float mass = this.mass;
        DebugUtil.DevAssert(mass <= maximum_capacity, "Effective mass cannot be greater than capacity!");
        return Mathf.Max(0f, maximum_capacity - mass);
    }

Expected behavior

Expect a succesful patch.

Screenshots / Code

See above. Can forward entire patched code file if needed.

Runtime environment (please complete the following information):

Windows 10, 64 bit. .NET 3.5 [assembly: AssemblyVersion("1.2.0.1")]

Additional context

"mono" appears at various times in the ONI decoompiled, so I suspect it is a mono app.

CannibarRechter commented 4 years ago

Update. Here is my entire attempt at patching it. Please note the annotations below (unrelated). I have used that method. I have used variations of explicit calls as well. You are just seeing the most current manual way of me getting at nested type. I've done it with "." notation also.

using Harmony; using System; using System.Reflection; using System.Collections.Generic; using Klei;

namespace HeatExchange { public class HeatExchangerPatches { public static class Mod_OnLoad { public static void OnLoad() { HarmonyInstance harmony = HarmonyInstance.Create("oni.cr.hex"); HarmonyInstance.DEBUG = true;

            Debug.Log("--> Heat Exchanger attempting patch...");

            //MethodInfo origMethod = AccessTools.Method(typeof(ConduitFlow.ConduitContents), "GetEffectiveCapacity");

            MethodInfo origMethod =  AccessTools.Method(typeof(ConduitFlow).GetNestedType("ConduitContents", BindingFlags.Public | BindingFlags.Instance), "GetEffectiveCapacity");

            HarmonyMethod replaceMethod = new HarmonyMethod( typeof(ConduitContentsPatch), "Prefix" );  

            Debug.Log($"--> origMethod = {origMethod} replaceMethod = {replaceMethod}");

            try{
                harmony.Patch(
                    origMethod,
                    replaceMethod
                );              
            }

            catch( Exception e )
            {
                Debug.Log($"##### Exception { e }");
                throw e;
            }

            Debug.Log("--> Heat Exchanger Mod loaded.");
        }
    }
    public static class ConduitContentsPatch
    {
        public static void Prefix( ConduitFlow.ConduitContents __instance, float maximum_capacity )
        {
            float mass = __instance.mass;
            Debug.Assert( mass <= maximum_capacity );
        }
    }

    [HarmonyPatch(typeof(GeneratedBuildings))]
    [HarmonyPatch(nameof(GeneratedBuildings.LoadGeneratedBuildings))]
    public static class GeneratedBuildings_LoadGeneratedBuildings_Patch
    {
        public static void Prefix()
        {
            StringUtils.AddBuildingStrings(HeatExchangerConfig.ID, HeatExchangerConfig.DisplayName, HeatExchangerConfig.Description, HeatExchangerConfig.Effect);
            BuildingUtils.AddBuildingToPlanScreen("Utilities", HeatExchangerConfig.ID);
        }
    }

    [HarmonyPatch(typeof(Db))]
    [HarmonyPatch("Initialize")]
    public static class Db_Initialize_Patch
    {
        public static void Prefix()
        {
            BuildingUtils.AddBuildingToTechnology("LiquidTemperature", HeatExchangerConfig.ID);
        }
    }
}

}

CannibarRechter commented 4 years ago

Also, I turned off all other mods "just to be sure," except for the mod manager mod.

bernardd commented 4 years ago

I've just hit exactly this same issue attempting to move my mod over to Harmony.

Environment: Windows 10, 64 bit. Pretty sure I'm running .NET 4 Harmony version: 1.2.0.1

Name of game: Cities:Skylines

The output from the C:S debug window is below:

The Mod C:\Users\XXXXX\AppData\Local\Colossal Order\Cities_Skylines\Addons\Mods\Crossings [0Harmony.dll, Crossings.dll] has caused an error [ModException]

Details:
System.FormatException: Method NetNode.CalculateNode(System.UInt16) cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) NetNode:CalculateNode_Patch1 (object,uint16): IL_0758: callvirt  0x0000011b

  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.<PatchAll>b__9_0 (System.Type type) [0x00000] in <filename unknown>:0 
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0 
  at Crossings.Loader.OnCreated (ILoading loading) [0x00000] in <filename unknown>:0 
  at LoadingWrapper.OnLoadingExtensionsCreated () [0x00000] in <filename unknown>:0 

I'm running it with no other mods active, and the patch in question is pretty trivial:

using Harmony;

namespace Crossings
{
    [HarmonyPatch(typeof(NetNode))]
    [HarmonyPatch(nameof(NetNode.CalculateNode))]
    class CalculateNode
    {
        static void Postfix(ref NetNode __instance)
        {
            bool isCrossing = (__instance.m_flags & (NetNode.Flags)Crossings.CrossingFlag) != NetNode.Flags.None;
            if ((__instance.m_flags & (NetNode.Flags.Junction | NetNode.Flags.Bend | NetNode.Flags.End)) != NetNode.Flags.None)
            {
                __instance.m_flags |= ~(NetNode.Flags)Crossings.CrossingFlag;
            }

            if (isCrossing)
            {
                __instance.m_flags |= NetNode.Flags.Junction;
            }
        }
    }
}

Please let me know if there's any more info I can provide to help debug this.

pardeike commented 4 years ago

You can start by testing with Harmony 2.0b (no release yet, needs to be build from master). This will help to identify if this is an issue that was fixed after 1.2.0.1

bernardd commented 4 years ago

It does indeed appear to be resolved in 2.0b - thanks for that.

pardeike commented 4 years ago

Due to time constraints I cannot update v1.2.0.1 and rather make sure 2.0 comes out of beta as soon as possible.