prog
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled

This commit is contained in:
2026-01-05 15:01:51 +02:00
parent 56a85f6ac0
commit 43c4d45080
3 changed files with 226 additions and 45 deletions

View File

@@ -40,6 +40,7 @@ namespace FlaxEngine.Interop
object managed = ManagedHandle.FromIntPtr(unmanaged).Target;
if (managed is ManagedArray managedArray)
{
throw new Exception("not supported");
var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal);
managed = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType);
managedArrayHandle.Free();

View File

@@ -807,8 +807,11 @@ namespace FlaxEngine.Interop
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
if (exception.InnerException != null &&
(exception.TargetSite.ReflectedType.Name == "MethodInvoker" || exception.TargetSite.ReflectedType.Name == "MethodBaseInvoker"))
{
realException = exception.InnerException;
}
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));

View File

@@ -21,6 +21,7 @@ using System.Threading;
using System.Buffers;
using System.Runtime.InteropServices.Marshalling;
using FlaxEngine.Utilities;
using System.Diagnostics;
namespace FlaxEngine.Interop
{
@@ -412,7 +413,86 @@ namespace FlaxEngine.Interop
internal static ConcurrentDictionary<Type, MarshalToNativeDelegate> toNativeMarshallers = new ConcurrentDictionary<Type, MarshalToNativeDelegate>(1, 3);
internal static ConcurrentDictionary<Type, MarshalToNativeFieldDelegate> toNativeFieldMarshallers = new ConcurrentDictionary<Type, MarshalToNativeFieldDelegate>(1, 3);
internal static ConcurrentDictionary<Type, (Type, Func<object, object>)> _marshallers = new(1, 3);
internal static ConcurrentDictionary<Type, (Type, Func<object, object>, Func<IntPtr, object>)> _marshallers = new(1, 3);
internal static class NewMarshalHelperValueType<TDest, TSource> where TSource : struct
{
internal static object NativeToManaged9(object src)
{
return NewMarshalHelper<TDest, TSource>.NativeToManaged9(ref Unsafe.Unbox<TSource>(src));
}
}
internal static class NewMarshalHelperReferenceType<TDest, TSource> where TSource : class
{
internal static object NativeToManaged9(object src)
{
TSource tsrc = Unsafe.As<TSource>(src);
return NewMarshalHelper<TDest, TSource>.NativeToManaged9(ref tsrc);
}
}
internal static class NewMarshalHelper<TDest, TSource>
{
internal static Func<TSource, TDest> _nativeToManagedFunc;
internal static Type _pointerType;
internal static void Init(MethodInfo nativeToManagedMethod)
{
//Assert.IsNull(_nativeToManagedFunc);
//var internalType = nativeToManagedMethod.GetParameters()[0].ParameterType;
//var types = new Type[] { marshalType, internalType };
//var convertDelegate = method.CreateDelegate(typeof(ToManagedDelegate<,>).MakeGenericType(types));
if (_nativeToManagedFunc == null && nativeToManagedMethod != null)
_nativeToManagedFunc = nativeToManagedMethod.CreateDelegate<Func<TSource, TDest>>();
_pointerType = typeof(TDest).MakePointerType();
}
internal static TDest NativeToManaged9(ref TSource src)
{
return _nativeToManagedFunc(src);
}
internal static object NativeToManagedIntPtr(TSource src)
{
// typeof(TSource) == typeof(IntPtr)
return _nativeToManagedFunc(src);
}
internal static object NativeToManagedPointer(IntPtr src)
{
//byte* asd = null;
//return asd;
return Pointer.Box(src.ToPointer(), _pointerType);
}
internal static object NativeToManagedByPass(TSource src) => src;
internal static object NativeToManaged(IntPtr src)
{
// typeof(TSource) != typeof(IntPtr)
TSource native = Marshal.PtrToStructure<TSource>(src);
return _nativeToManagedFunc(native);
}
internal static object NativeToManagedDirect(IntPtr src)
{
// typeof(TSource) != typeof(IntPtr)
TSource native = Unsafe.Read<TSource>(src.ToPointer());
return native;
}
}
internal static object MarshalToManaged2(IntPtr nativePtr, Type type)
{
@@ -432,6 +512,10 @@ namespace FlaxEngine.Interop
{
if (type == typeof(bool))
return typeof(BooleanMarshaller);
else if (type == typeof(string))
return typeof(StringMarshaller); // UTF-16 strings via MMethod::Invoke
else if (type == typeof(object))
return typeof(AsManagedHandleMarshaller);
else if (type.IsArray)
return null;
@@ -441,12 +525,13 @@ namespace FlaxEngine.Interop
return marshallerType;
}
static (Type, Func<object, object>) Factory2(Type type)
static (Type, Func<object, object>, Func<IntPtr, object>) Factory2(Type type)
{
Type marshalType = type;
if (marshalType.IsByRef)
marshalType = marshalType.GetElementType();
else if (marshalType == typeof(byte*) || marshalType == typeof(char*))
if (marshalType == typeof(byte*) || marshalType == typeof(char*))
{
}
else if (marshalType.IsPointer)
@@ -473,17 +558,63 @@ namespace FlaxEngine.Interop
{
if (marshalType == typeof(IntPtr))
{
//var setup = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(IntPtr), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.Init), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
//setup.Invoke(null, [method]);
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(IntPtr), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedByPass), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
Func<object, object> func = (obj) => obj;
return (typeof(IntPtr), func);
return (typeof(IntPtr), func, convdirectdel);
}
else if (marshalType == typeof(ManagedHandle))
{
var methody = typeof(ManagedHandle).GetMethod(nameof(ManagedHandle.FromIntPtr), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var setup = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(ManagedHandle), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.Init), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
setup.Invoke(null, [methody]);
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(ManagedHandle), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedIntPtr), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
Func<object, object> func = (obj) => ManagedHandle.FromIntPtr((IntPtr)obj);
return (typeof(IntPtr), func);
return (typeof(IntPtr), func, convdirectdel);
}
else if (marshalType.IsPointer)
{
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(IntPtr), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedDirect), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
Func<object, object> func = (obj) => obj;
return (typeof(IntPtr), func, convdirectdel);
//return (null, null, null);
/*var setup = typeof(NewMarshalHelper<,>).MakeGenericType([marshalType.GetElementType(), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.Init), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
setup.Invoke(null, [null]);
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([marshalType.GetElementType(), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedPointer), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
return (marshalType, null, convdirectdel);*/
}
else if (marshalType.IsPrimitive || marshalType.IsEnum)
{
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([marshalType, marshalType]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedDirect), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
return (marshalType, null, convdirectdel);
}
else if (!marshalType.IsArray && IsBlittable(marshalType))
{
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([marshalType, marshalType]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedDirect), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
return (marshalType, null, convdirectdel);
}
/*else if (marshalType == typeof(object))
{
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType([typeof(ManagedHandle), typeof(IntPtr)]).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedIntPtr), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
Func<object, object> func = (obj) => (IntPtr)obj != IntPtr.Zero ? ManagedHandle.FromIntPtr((IntPtr)obj).Target : null;
return (typeof(IntPtr), func, null);
}*/
}
if (marshallerType == null)
return (null, null);
return (null, null, null);
MethodInfo method = marshallerType.GetMethod("ConvertToManaged", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
if (method == null)
@@ -502,71 +633,116 @@ namespace FlaxEngine.Interop
}
}
return (null, null);
return (null, null, null);
}
var internalType = method.GetParameters()[0].ParameterType;
var types = new Type[] { marshalType, internalType };
var convertDelegate = method.CreateDelegate(typeof(ToManagedDelegate<,>).MakeGenericType(types));
if (internalType.IsStructure())
var minit = typeof(NewMarshalHelper<,>).MakeGenericType(types).GetMethod(nameof(NewMarshalHelper<,>.Init), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
minit.Invoke(null, [method]);
var conv = typeof(NewMarshalHelper<,>).MakeGenericType(types).GetMethod(nameof(NewMarshalHelper<,>.NativeToManaged), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdel = conv.CreateDelegate<Func<IntPtr, object>>();
if (internalType == typeof(IntPtr))
{
var convdirect = typeof(NewMarshalHelper<,>).MakeGenericType(types).GetMethod(nameof(NewMarshalHelper<,>.NativeToManagedIntPtr), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var convdirectdel = convdirect.CreateDelegate<Func<IntPtr, object>>();
Func<object, object> func =
(obj) => convertDelegate.DynamicInvoke(obj);
return (internalType, func, convdirectdel);
}
else if (internalType.IsStructure())
{
Func<object, object> func =
(obj) => convertDelegate.DynamicInvoke(obj);
return (internalType, func);
return (internalType, func, convdel);
}
else
{
Func<object, object> func =
(obj) => convertDelegate.DynamicInvoke(obj);
return (internalType, func);
return (internalType, func, convdel);
}
}
var type2 = type.IsByRef ? type.GetElementType() : type;
/*if (type2.IsPrimitive)
{
if (type.IsByRef && type2 == typeof(IntPtr))
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
//ref byte src = ref Unsafe.AsRef<byte>(nativePtr.ToPointer());
//var ret = RuntimeHelpers.Box(ref src, type.TypeHandle);
var ret = Marshal.PtrToStructure(nativePtr, type2);
return ret;
}
else if (type2.IsEnum)
{
if (type.IsByRef)
type2 = type2;
ref byte src = ref Unsafe.AsRef<byte>(nativePtr.ToPointer());
var ret = RuntimeHelpers.Box(ref src, type2.TypeHandle);
return ret;
}*/
if (!_marshallers.TryGetValue(type, out var marsh))
marsh = _marshallers.GetOrAdd(type, Factory2);
if (!toManagedMarshallers.TryGetValue(type, out var deleg))
deleg = toManagedMarshallers.GetOrAdd(type, Factory);
if (marsh.Item2 != null)
{
object conv1 = deleg(nativePtr, type.IsByRef && false);
if (type.IsByRef && marsh.Item1 == typeof(IntPtr))
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
object value = marsh.Item1 == typeof(IntPtr) ? nativePtr : Marshal.PtrToStructure(nativePtr, marsh.Item1);//Unsafe.Read<byte>(nativePtr.ToPointer());
object conv2 = marsh.Item2(value);
if (conv1 is null && conv2 is null)
{ }
else if (!conv1.Equals(conv2))
Assert.IsTrue(conv1.Equals(conv2));
return conv2;
}
else if (type2 == typeof(bool))
if (marsh.Item3 != null)
{
//object conv1 = deleg(nativePtr, type.IsByRef && false);
byte value = Unsafe.Read<byte>(nativePtr.ToPointer());
object conv2 = BooleanMarshaller.ConvertToManaged(value);
//Assert.IsTrue(conv1.Equals(conv2));
return conv2;
}
else if (type2 == typeof(ManagedHandle))
{
//ManagedHandle conv1 = (ManagedHandle)deleg(nativePtr, type.IsByRef && false);
ManagedHandle conv2 = ManagedHandle.FromIntPtr(nativePtr);
//Assert.IsTrue(conv1.Equals(conv2));
return conv2;
}
else if (type2 == typeof(int) || type2.IsEnum ||
type2 == typeof(IntPtr) || type2 == typeof(Float2) || type2 == typeof(Float3) || type2 == typeof(Guid))
{
if (type.IsByRef && marsh.Item1 == typeof(IntPtr))
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
if (true && marsh.Item3 != null)
{
// TODO: CustomMarshaller.Free()
return marsh.Item3(nativePtr);
}
else
{
object value = marsh.Item1 == typeof(IntPtr) ? nativePtr : Marshal.PtrToStructure(nativePtr, marsh.Item1);
object conv2 = marsh.Item2(value);
// TODO: CustomMarshaller.Free()
return conv2;
}
/*if (conv1 is null && conv2 is null)
{ }
else if (!conv1.Equals(conv2))
Assert.IsTrue(conv1.Equals(conv2));*/
}
if (type2.IsStructure())
{
var ret = Marshal.PtrToStructure(nativePtr, type2);
return ret;
}
if (type2 == typeof(int) || type2.IsEnum
|| type2 == typeof(IntPtr) || type2 == typeof(Float2) || type2 == typeof(Float3)
|| type2 == typeof(long) || type2 == typeof(sbyte)
|| type2 == typeof(uint) || type2 == typeof(ulong) || type2 == typeof(ushort) || type2 == typeof(byte)
|| type2 == typeof(float) || type2 == typeof(double))
{
// TODO
}
else if (type2 == typeof(byte*) || type2 == typeof(Guid) || type2.IsArray)
{
// TODO
}
else
{
}
if (!toManagedMarshallers.TryGetValue(type, out var deleg))
deleg = toManagedMarshallers.GetOrAdd(type, Factory);
return deleg(nativePtr, type.IsByRef && false);
}
@@ -1640,7 +1816,8 @@ namespace FlaxEngine.Interop
internal static void ToNativeString(ref string managedValue, IntPtr nativePtr)
{
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNative/*Weak*/(managedValue));
//Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNative/*Weak*/(managedValue));
Unsafe.Write<NativeString>(nativePtr.ToPointer(), new NativeString(managedValue));
}
internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr)