public static void AssignGcHandle(IntPtr pointer, GCHandle gcHandle) { var handleAsPointer = GCHandle.ToIntPtr(gcHandle); if (pointer == IntPtr.Zero) { throw new NullReferenceException(nameof(pointer)); } var objectKlass = (Il2CppClass *)IL2CPP.il2cpp_object_get_class(pointer); var targetGcHandlePointer = IntPtr.Add(pointer, (int)UnityVersionHandler.Wrap(objectKlass).InstanceSize - IntPtr.Size); *(IntPtr *)targetGcHandlePointer = handleAsPointer; }
public static void RegisterTypeInIl2Cpp <T>() where T : class { var type = typeof(T); if (type.IsGenericType || type.IsGenericTypeDefinition) { throw new ArgumentException($"Type {type} is generic and can't be used in il2cpp"); } var currentPointer = Il2CppClassPointerStore <T> .NativeClassPtr; if (currentPointer != IntPtr.Zero) { throw new ArgumentException($"Type {type} is already registered in il2cpp"); } var baseType = type.BaseType; var baseClassPointer = UnityVersionHandler.Wrap((Il2CppClass *)ReadClassPointerForType(baseType)); if (baseClassPointer == null) { throw new ArgumentException($"Base class {baseType} of class {type} is not registered in il2cpp"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.valuetype) != 0 || (baseClassPointer.Bitfield1 & ClassBitfield1.enumtype) != 0) { throw new ArgumentException($"Base class {baseType} is value type and can't be inherited from"); } if ((baseClassPointer.Bitfield1 & ClassBitfield1.is_generic) != 0) { throw new ArgumentException($"Base class {baseType} is generic and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_SEALED) != 0) { throw new ArgumentException($"Base class {baseType} is sealed and can't be inherited from"); } if ((baseClassPointer.Flags & Il2CppClassAttributes.TYPE_ATTRIBUTE_INTERFACE) != 0) { throw new ArgumentException($"Base class {baseType} is an interface and can't be inherited from"); } lock (InjectedTypes) if (!InjectedTypes.Add(typeof(T).FullName)) { throw new ArgumentException($"Type with FullName {typeof(T).FullName} is already injected. Don't inject the same type twice, or use a different namespace"); } if (ourOriginalTypeToClassMethod == null) { HookClassFromType(); } var classPointer = UnityVersionHandler.NewClass(baseClassPointer.VtableCount); classPointer.Image = FakeImage; classPointer.Parent = baseClassPointer.ClassPointer; classPointer.ElementClass = classPointer.Class = classPointer.CastClass = classPointer.ClassPointer; classPointer.NativeSize = -1; classPointer.ActualSize = classPointer.InstanceSize = baseClassPointer.InstanceSize + (uint)IntPtr.Size; classPointer.Bitfield1 = ClassBitfield1.initialized | ClassBitfield1.initialized_and_no_error | ClassBitfield1.size_inited; classPointer.Bitfield2 = ClassBitfield2.has_finalize | ClassBitfield2.is_vtable_initialized; classPointer.Name = Marshal.StringToHGlobalAnsi(type.Name); classPointer.Namespace = Marshal.StringToHGlobalAnsi(type.Namespace); classPointer.ThisArg.type = classPointer.ByValArg.type = Il2CppTypeEnum.IL2CPP_TYPE_CLASS; classPointer.ThisArg.mods_byref_pin = 64; classPointer.Flags = baseClassPointer.Flags; // todo: adjust flags? var eligibleMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).Where(IsMethodEligible).ToArray(); var methodCount = 2 + eligibleMethods.Length; // 1 is the finalizer, 1 is empty ctor classPointer.MethodCount = (ushort)methodCount; var methodPointerArray = (Il2CppMethodInfo **)Marshal.AllocHGlobal(methodCount * IntPtr.Size); classPointer.Methods = methodPointerArray; methodPointerArray[0] = ConvertStaticMethod(FinalizeDelegate, "Finalize", classPointer); methodPointerArray[1] = ConvertStaticMethod(CreateEmptyCtor(type), ".ctor", classPointer); for (var i = 0; i < eligibleMethods.Length; i++) { var methodInfo = eligibleMethods[i]; methodPointerArray[i + 2] = ConvertMethodInfo(methodInfo, classPointer); } var vTablePointer = (VirtualInvokeData *)classPointer.VTable; var baseVTablePointer = (VirtualInvokeData *)baseClassPointer.VTable; classPointer.VtableCount = baseClassPointer.VtableCount; for (var i = 0; i < classPointer.VtableCount; i++) { vTablePointer[i] = baseVTablePointer[i]; if (Marshal.PtrToStringAnsi(vTablePointer[i].method->name) == "Finalize") // slot number is not static { vTablePointer[i].method = methodPointerArray[0]; vTablePointer[i].methodPtr = methodPointerArray[0]->methodPointer; } } var newCounter = Interlocked.Decrement(ref ourClassOverrideCounter); FakeTokenClasses[newCounter] = classPointer.Pointer; classPointer.ByValArg.data = classPointer.ThisArg.data = (IntPtr)newCounter; RuntimeSpecificsStore.SetClassInfo(classPointer.Pointer, true, true); Il2CppClassPointerStore <T> .NativeClassPtr = classPointer.Pointer; LogSupport.Info($"Registered mono type {typeof(T)} in il2cpp domain"); }