在不同dll里面注入,fix不同Dll里面的函数,当这些函数具有同样参数类型的匿名函数会出现报错
项目里分了几个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)

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

追下去发现
在创建delegate的过程中
实例类型和方法的声明的类runtimetype不同
最后抛出了异常

所以猜测问题是出现在Utils.TryAdapterToDelegate里,在不同的dll里面调用方法的时候 return method1 == (MethodInfo) null ? (Delegate) null : Delegate.CreateDelegate(delegateType, obj, method1);这句里面的obj的实例是不同的,所以它本身的runtimetype也是不同的,实例类型是ILFixDynamicMethodWrapper。但是缓存methodinfo的时候是按照第一个实例来的,就导致了上面出现的报错信息: Fatal ArgumentException: method arguments are incompatible
不知猜想是否正确,请问有什么合适的解决方法吗
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);
}
}