public static TIl2Cpp ConvertDelegate <TIl2Cpp>(Delegate @delegate) where TIl2Cpp : Il2CppObjectBase { if (@delegate == null) { return(null); } if (!typeof(Il2CppSystem.Delegate).IsAssignableFrom(typeof(TIl2Cpp))) { throw new ArgumentException($"{typeof(TIl2Cpp)} is not a delegate"); } var managedInvokeMethod = @delegate.GetType().GetMethod("Invoke") !; var parameterInfos = managedInvokeMethod.GetParameters(); foreach (var parameterInfo in parameterInfos) { var parameterType = parameterInfo.ParameterType; if (parameterType.IsGenericParameter) { throw new ArgumentException($"Delegate has unsubstituted generic parameter ({parameterType}) which is not supported"); } if (parameterType.BaseType == typeof(ValueType)) { throw new ArgumentException($"Delegate has parameter of type {parameterType} (non-blittable struct) which is not supported"); } } var classTypePtr = Il2CppClassPointerStore <TIl2Cpp> .NativeClassPtr; if (classTypePtr == IntPtr.Zero) { throw new ArgumentException($"Type {typeof(TIl2Cpp)} has uninitialized class pointer"); } if (Il2CppClassPointerStore <Il2CppToMonoDelegateReference> .NativeClassPtr == IntPtr.Zero) { ClassInjector.RegisterTypeInIl2Cpp <Il2CppToMonoDelegateReference>(); } var il2CppDelegateType = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(classTypePtr)); var nativeDelegateInvokeMethod = il2CppDelegateType.GetMethod("Invoke"); var nativeParameters = nativeDelegateInvokeMethod.GetParameters(); if (nativeParameters.Count != parameterInfos.Length) { throw new ArgumentException($"Managed delegate has {parameterInfos.Length} parameters, native has {nativeParameters.Count}, these should match"); } for (var i = 0; i < nativeParameters.Count; i++) { var nativeType = nativeParameters[i].ParameterType; var managedType = parameterInfos[i].ParameterType; if (nativeType.IsPrimitive || managedType.IsPrimitive) { if (nativeType.FullName != managedType.FullName) { throw new ArgumentException($"Parameter type mismatch at parameter {i}: {nativeType.FullName} != {managedType.FullName}"); } continue; } var classPointerFromManagedType = (IntPtr)typeof(Il2CppClassPointerStore <>).MakeGenericType(managedType) .GetField(nameof(Il2CppClassPointerStore <int> .NativeClassPtr)).GetValue(null); var classPointerFromNativeType = IL2CPP.il2cpp_class_from_type(nativeType._impl.value); if (classPointerFromManagedType != classPointerFromNativeType) { throw new ArgumentException($"Parameter type at {i} has mismatched native type pointers; types: {nativeType.FullName} != {managedType.FullName}"); } if (nativeType.IsByRef || managedType.IsByRef) { throw new ArgumentException($"Parameter at {i} is passed by reference, this is not supported"); } } var signature = new MethodSignature(nativeDelegateInvokeMethod, true); var managedTrampoline = GetOrCreateNativeToManagedTrampoline(signature, nativeDelegateInvokeMethod, managedInvokeMethod); var nativeDelegatePtr = IL2CPP.il2cpp_object_new(classTypePtr); var converted = new Il2CppSystem.Delegate(nativeDelegatePtr); converted.method_ptr = Marshal.GetFunctionPointerForDelegate(managedTrampoline); converted.method_info = nativeDelegateInvokeMethod; // todo: is this truly a good hack? var methodInfoSize = Marshal.SizeOf <Il2CppMethodInfo>(); var methodInfoPointer = Marshal.AllocHGlobal(methodInfoSize); var methodInfo = (Il2CppMethodInfo *)methodInfoPointer; *methodInfo = default; // zero out everything converted.method = methodInfoPointer; methodInfo->methodPointer = converted.method_ptr; methodInfo->invoker_method = IntPtr.Zero; methodInfo->parameters_count = (byte)parameterInfos.Length; methodInfo->slot = ushort.MaxValue; methodInfo->extra_flags = MethodInfoExtraFlags.is_marshalled_from_native; converted.m_target = new Il2CppToMonoDelegateReference(@delegate, methodInfoPointer); return(converted.Cast <TIl2Cpp>()); }
public static TIl2Cpp ConvertDelegate <TIl2Cpp>(Delegate @delegate) where TIl2Cpp : Il2CppObjectBase { if (@delegate == null) { return(null); } if (!typeof(Il2CppSystem.Delegate).IsAssignableFrom(typeof(TIl2Cpp))) { throw new ArgumentException($"{typeof(TIl2Cpp)} is not a delegate"); } var managedInvokeMethod = @delegate.GetType().GetMethod("Invoke") !; var parameterInfos = managedInvokeMethod.GetParameters(); foreach (var parameterInfo in parameterInfos) { var parameterType = parameterInfo.ParameterType; if (parameterType.IsGenericParameter) { throw new ArgumentException($"Delegate has unsubstituted generic parameter ({parameterType}) which is not supported"); } if (parameterType.BaseType == typeof(ValueType)) { throw new ArgumentException($"Delegate has parameter of type {parameterType} (non-blittable struct) which is not supported"); } } var classTypePtr = Il2CppClassPointerStore <TIl2Cpp> .NativeClassPtr; if (classTypePtr == IntPtr.Zero) { throw new ArgumentException($"Type {typeof(TIl2Cpp)} has uninitialized class pointer"); } if (Il2CppClassPointerStore <Il2CppToMonoDelegateReference> .NativeClassPtr == IntPtr.Zero) { ClassInjector.RegisterTypeInIl2Cpp <Il2CppToMonoDelegateReference>(); } var il2CppDelegateType = Il2CppSystem.Type.internal_from_handle(IL2CPP.il2cpp_class_get_type(classTypePtr)); var nativeDelegateInvokeMethod = il2CppDelegateType.GetMethod("Invoke"); var nativeParameters = nativeDelegateInvokeMethod.GetParameters(); if (nativeParameters.Count != parameterInfos.Length) { throw new ArgumentException($"Managed delegate has {parameterInfos.Length} parameters, native has {nativeParameters.Count}, these should match"); } for (var i = 0; i < nativeParameters.Count; i++) { var nativeType = nativeParameters[i].ParameterType; var managedType = parameterInfos[i].ParameterType; if (nativeType.IsPrimitive || managedType.IsPrimitive) { if (nativeType.FullName != managedType.FullName) { throw new ArgumentException($"Parameter type mismatch at parameter {i}: {nativeType.FullName} != {managedType.FullName}"); } continue; } var classPointerFromManagedType = (IntPtr)typeof(Il2CppClassPointerStore <>).MakeGenericType(managedType) .GetField(nameof(Il2CppClassPointerStore <int> .NativeClassPtr)).GetValue(null); var classPointerFromNativeType = IL2CPP.il2cpp_class_from_type(nativeType._impl.value); if (classPointerFromManagedType != classPointerFromNativeType) { throw new ArgumentException($"Parameter type at {i} has mismatched native type pointers; types: {nativeType.FullName} != {managedType.FullName}"); } if (nativeType.IsByRef || managedType.IsByRef) { throw new ArgumentException($"Parameter at {i} is passed by reference, this is not supported"); } } var signature = new MethodSignature(nativeDelegateInvokeMethod, true); var managedTrampoline = GetOrCreateNativeToManagedTrampoline(signature, nativeDelegateInvokeMethod, managedInvokeMethod); var methodInfo = UnityVersionHandler.NewMethod(); methodInfo.MethodPointer = Marshal.GetFunctionPointerForDelegate(managedTrampoline); methodInfo.ParametersCount = (byte)parameterInfos.Length; methodInfo.Slot = ushort.MaxValue; methodInfo.IsMarshalledFromNative = true; var delegateReference = new Il2CppToMonoDelegateReference(@delegate, methodInfo.Pointer); Il2CppSystem.Delegate converted; if (UnityVersionHandler.MustUseDelegateConstructor) { converted = ((TIl2Cpp)Activator.CreateInstance(typeof(TIl2Cpp), delegateReference.Cast <Object>(), methodInfo.Pointer)).Cast <Il2CppSystem.Delegate>(); } else { var nativeDelegatePtr = IL2CPP.il2cpp_object_new(classTypePtr); converted = new Il2CppSystem.Delegate(nativeDelegatePtr); } converted.method_ptr = methodInfo.MethodPointer; converted.method_info = nativeDelegateInvokeMethod; // todo: is this truly a good hack? converted.method = methodInfo.Pointer; converted.m_target = delegateReference; if (UnityVersionHandler.MustUseDelegateConstructor) { // U2021.2.0+ hack in case the constructor did the wrong thing anyway converted.invoke_impl = converted.method_ptr; converted.method_code = converted.m_target.Pointer; } return(converted.Cast <TIl2Cpp>()); }