pardeike / Harmony

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

Error trying to patch base virtual method #54

Closed Entoarox closed 6 years ago

Entoarox commented 6 years ago

I am trying to patch a base virtual method, but the following error occurs when I do so:

System.NotSupportedException: Incorrect MethodAttributes or CallingConventions for DynamicMethod. Only public, static and standard are supported
   by System.Reflection.Emit.DynamicMethod.CheckConsistency(MethodAttributes attributes, CallingConventions callingConvention)
   by System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark)
   by System.Reflection.Emit.DynamicMethod..ctor(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
   by Harmony.DynamicTools.CreateDynamicMethod(MethodBase original, String suffix)
   by Harmony.MethodPatcher.CreatePatchedMethod(MethodBase original, List`1 prefixes, List`1 postfixes, List`1 transpilers)
   by Harmony.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo)
   by Harmony.PatchProcessor.Patch()
   by Harmony.HarmonyInstance.<PatchAll>b__6_0(Type type)
   by Harmony.CollectionExtensions.Do[T](IEnumerable`1 sequence, Action`1 action)
   by Harmony.HarmonyInstance.PatchAll(Assembly assembly)
   by Entoarox.FurnitureAnywhere.FurnitureAnywhereMod.Entry(IModHelper helper)
   by StardewModdingAPI.Program.LoadMods(IModMetadata[] mods, JsonHelper jsonHelper, SContentManager contentManager) in C:\source\_Stardew\SMAPI\src\SMAPI\Program.cs:line 808

(Please excuse any translation errors in the error message, I had to translate the error message to english so it might not be 100% correct)

The patch code is as follows:

    [HarmonyPatch(typeof(GameLocation))]
    [HarmonyPatch("isCollidingPosition")]
    [HarmonyPatch(new Type[] { typeof(Microsoft.Xna.Framework.Rectangle), typeof(xTile.Dimensions.Rectangle), typeof(bool), typeof(int), typeof(bool), typeof(Character), typeof(bool), typeof(bool), typeof(bool) })]
    static class LocationPatch
    {
        public static bool Prefix(ref bool __return, GameLocation __instance, Microsoft.Xna.Framework.Rectangle position)
        {
            foreach (AnywhereFurniture current in __instance.objects.Values.Select(a => a as AnywhereFurniture))
                if (current.furniture_type != 12 && current.getBoundingBox(current.tileLocation).Intersects(position))
                {
                    __return = true;
                    return true;
                }
            return false;
        }
    }

I am probably making some mistake here on my end, but the wiki has been useless in helping solve this, so I am stuck.

pardeike commented 6 years ago

Please have a look at #2 and #27 since they have a similar error message. Your problem is not the method but the .NET version or your environment that causes this.

Entoarox commented 6 years ago

I am using VS2017 to compile the DLL, it is compiled for .NET 4.5 (A must since that is what Stardew Valley [which this is intended for] is written in) and running it on a windows 7 PC.

I have no clue how to solve this, so any help you can give would be much appreciated!

pardeike commented 6 years ago

I tried to replicate your issue and found that you wrote ref bool __return there in fact it is ref bool __result. Not sure if this is the problem but at least make sure that you correct this.

pardeike commented 6 years ago

Unfortunately, I get an access violation for any .NET version higher than 3.5 Here is my minimal test code:

using Harmony;
using System;

namespace HarmonyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var harmony = HarmonyInstance.Create("test");
            harmony.PatchAll(typeof(Program).Assembly);

            var instance = new Subclass();
            instance.SomeMethod();
            Console.ReadKey();
        }
    }

    public class Subclass : BaseClass
    {
        public override void SomeMethod()
        {
            Console.WriteLine("Subclass");
            base.SomeMethod();
        }
    }

    public class BaseClass
    {
        public virtual void SomeMethod()
        {
            Console.WriteLine("BaseClass");
        }
    }

    [HarmonyPatch(typeof(BaseClass))]
    [HarmonyPatch("SomeMethod")]
    static class LocationPatch
    {
        public static void Prefix()
        {
            Console.WriteLine("Prefix");
        }
    }
}
pardeike commented 6 years ago

I just found out that if you build Harmony in Release and run your .NET test code in Release too, it does not trigger an access violation exception and it can patch virtual methods just fine.