/*************************************************************************************************** Copyright (C) 2023 The Qt Company Ltd. SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ***************************************************************************************************/ using System.Reflection; using System.Runtime.InteropServices; namespace Qt.DotNet { public partial class Adapter { internal static IntPtr GetRefPtrToObject(object obj, bool weakRef = false) { var objHandle = GCHandle.Alloc(obj, weakRef ? GCHandleType.Weak : GCHandleType.Normal); var objRefPtr = GCHandle.ToIntPtr(objHandle); ObjectRefs.TryAdd(objRefPtr, new ObjectRef(objHandle)); return objRefPtr; } internal static ObjectRef GetObjectRefFromPtr(IntPtr objRefPtr) { if (objRefPtr == IntPtr.Zero) throw new ArgumentNullException(nameof(objRefPtr)); if (!ObjectRefs.TryGetValue(objRefPtr, out var objRef)) return null; if (!objRef.IsValid) throw new ObjectDisposedException(nameof(objRefPtr)); return objRef; } /// /// Create a new reference (GCHandle) to the given object /// /// Native reference to target object. /// 'true' to create a weak ref.; 'false' otherwise (default) /// Native object reference /// public static IntPtr AddObjectRef(IntPtr objRefPtr, bool weakRef = false) { #if DEBUG // Compile-time signature check of delegate vs. method _ = new Delegates.AddObjectRef(AddObjectRef); #endif var objRef = GetObjectRefFromPtr(objRefPtr); if (objRef == null) throw new ArgumentException("Invalid object reference", nameof(objRefPtr)); var obj = objRef.Target; var objHandle = GCHandle.Alloc( obj, weakRef ? GCHandleType.Weak : GCHandleType.Normal); var newObjRefPtr = GCHandle.ToIntPtr(objHandle); ObjectRefs.TryAdd(newObjRefPtr, new ObjectRef(objHandle)); return newObjRefPtr; } /// /// Release object reference, as well as any associated instance method and event references /// /// Native reference to target object. /// 'true' if object ref. was released successfully; 'false' otherwise /// public static void FreeObjectRef(IntPtr objRefPtr) { #if DEBUG // Compile-time signature check of delegate vs. method _ = new Delegates.FreeObjectRef(FreeObjectRef); #endif if (!ObjectRefs.TryRemove(objRefPtr, out var objRef)) throw new ArgumentException("Invalid object reference", nameof(objRefPtr)); RemoveAllEventHandlers(objRef); var liveObjects = ObjectRefs.Values.Select(x => x.Target).ToList(); var isLive = liveObjects.Any(x => x.Equals(objRef.Target)); if (!isLive) { var deadMethods = DelegatesByMethod .Where(x => x.Key.Target.Equals(objRef.Target)) .Select(x => x.Value.FuncPtr) .ToList(); deadMethods.ForEach(FreeDelegateRef); } objRef.Handle.Free(); } /// /// Release reference to a type (i.e. instance of System.Type) /// /// Fully qualified name of type /// public static void FreeTypeRef(string typeName) { #if DEBUG // Compile-time signature check of delegate vs. method _ = new Delegates.FreeTypeRef(FreeTypeRef); #endif var type = Type.GetType(typeName) ?? throw new ArgumentException($"Type '{typeName}' not found", nameof(typeName)); var typeRefs = ObjectRefs .Where(x => x.Value.Target.Equals(type)) .ToList(); foreach (var typeRef in typeRefs) FreeObjectRef(typeRef.Key); var deadMethods = DelegatesByMethod .Where(x => x.Key.Target.Equals(type)) .Select(x => x.Value.FuncPtr) .ToList(); deadMethods.ForEach(FreeDelegateRef); } public static void FreeDelegateRef(IntPtr delRefPtr) { #if DEBUG // Compile-time signature check of delegate vs. method _ = new Delegates.FreeDelegateRef(FreeDelegateRef); #endif if (!DelegateRefs.TryRemove(delRefPtr, out var delegateRef)) return; DelegatesByMethod.TryRemove((delegateRef.Target, delegateRef.Method), out _); delegateRef.Ref.Handle.Free(); } public static IntPtr GetObject(IntPtr objRefPtr, string path) { #if DEBUG // Compile-time signature check of delegate vs. method _ = new Delegates.GetObject(GetObject); #endif var memberNames = path.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); var objRef = GetObjectRefFromPtr(objRefPtr); if (objRef == null) throw new ArgumentException("Invalid object reference", nameof(objRefPtr)); var obj = objRef.Target; foreach (var memberName in memberNames) { if (obj?.GetType()?.GetMember(memberName) is not MemberInfo[] members) throw new ArgumentException("Invalid object reference", memberName); foreach (var member in members) { if (member is PropertyInfo prop) { obj = prop.GetValue(obj); break; } else if (member is FieldInfo field) { obj = field.GetValue(obj); break; } throw new ArgumentException("Invalid object reference", memberName); } } return GetRefPtrToObject(obj); } } }