InjectFix icon indicating copy to clipboard operation
InjectFix copied to clipboard

在不同dll里面注入,fix不同Dll里面的函数,当这些函数具有同样参数类型的匿名函数会出现报错

Open 754100802 opened this issue 3 years ago • 1 comments

项目里分了几个dll,对这些dll的函数都进行了配置 在dll A里面对某方法 A进行ifix修复,方法 A里面声明了无参匿名函数,在dll B里面对某方法 B进行ifix修复,方法 B里面也声明了无参匿名函数

先游戏中先调用了方法A后再调用方法B会出现报错,反之也会出现,报错如下: 10:14:13.861 F02286 @T001 Fatal ArgumentException: method arguments are incompatible IFix.Core.VirtualMachine.Execute (IFix.Core.Instruction* pc, IFix.Core.Value* argumentBase, System.Object[] managedStack, IFix.Core.Value* evaluationStackBase, System.Int32 argsCount, System.Int32 methodIndex, System.Int32 refCount, IFix.Core.Value** topWriteBack) (at <7bfd294e701c4e569265a377b712dfc2>:0) IFix.Core.VirtualMachine.Execute (System.Int32 methodIndex, IFix.Core.Call& call, System.Int32 argsCount, System.Int32 refCount) (at <7bfd294e701c4e569265a377b712dfc2>:0) IFix.ILFixDynamicMethodWrapper.__Gen_Wrap_63 (System.Object P0, System.Object P1, System.Object P2) (at <9cadca0bf4564926a0c70bfea602861d>:0)

$M85UYIG 0}YRDAJ~8JBIYJ

检查过后发现了在第一次调用的时候会在IFix.Core.Utils的delegateAdptCache字典里面对方法类型进行缓存,在第二次调用的时候会从缓存里面取出同样类型的methodinfo然后执行Delegate.CreateDelegate(delegateType, obj, method1)就会报错,对应出问题的代码

E@(T)~W~_KWHRZOBTB )S

追下去发现 在创建delegate的过程中 image 实例类型和方法的声明的类runtimetype不同 @S0TK6M1SNNS9OXI 9OZI0T 最后抛出了异常 8`69B{N4}PR3MB3}2ZDTJNF

所以猜测问题是出现在Utils.TryAdapterToDelegate里,在不同的dll里面调用方法的时候 return method1 == (MethodInfo) null ? (Delegate) null : Delegate.CreateDelegate(delegateType, obj, method1);这句里面的obj的实例是不同的,所以它本身的runtimetype也是不同的,实例类型是ILFixDynamicMethodWrapper。但是缓存methodinfo的时候是按照第一个实例来的,就导致了上面出现的报错信息: Fatal ArgumentException: method arguments are incompatible

不知猜想是否正确,请问有什么合适的解决方法吗

754100802 avatar Oct 26 '22 02:10 754100802

ILFixDynamicMethodWrapper这个是Patch中动态生成的,每个程序集都会有,这里的这个实例类型虽然都是ILFixDynamicMethodWrapper,但它们属于不同的程序集,只是名字相同而已,可以改一下这里的代码,这个cache加一级程序集级别的缓存就行。

static Dictionary<string, Dictionary<Type, MethodInfo>> delegateAdptCache = new Dictionary<string, Dictionary<Type, MethodInfo>>();

public static Delegate TryAdapterToDelegate(object obj, Type delegateType, string perfix)
{
    MethodInfo method;
    if (!delegateAdptCache.TryGetValue(obj.GetType().Assembly.FullName, out var cache))
    {
        cache = new Dictionary<Type, MethodInfo>();
        delegateAdptCache.Add(obj.GetType().Assembly.FullName, cache);
    }

    if (!cache.TryGetValue(delegateType, out method))
    {
        MethodInfo delegateMethod = delegateType.GetMethod("Invoke");
        var methods = obj.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        for (int i = 0; i < methods.Length; i++)
        {
            if (methods[i].Name.StartsWith(perfix) && IsAssignable(delegateMethod, methods[i]))
            {
                method = methods[i];
                cache[delegateType] = method;
            }
        }
    }

    if (method == null)
    {
        return null;
    }
    else
    {
        return Delegate.CreateDelegate(delegateType, obj, method);
    }
}

oahceh avatar Mar 14 '23 10:03 oahceh