Fix managed arrays interop bug when passed via generic object parameter

#2613
This commit is contained in:
Wojtek Figat
2024-09-17 00:15:04 +02:00
parent f050621e3e
commit 0a22d5ab4d
2 changed files with 68 additions and 13 deletions

View File

@@ -15,8 +15,8 @@ namespace FlaxEngine.Interop
#if FLAX_EDITOR
[HideInEditor]
#endif
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
[CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))]
@@ -31,7 +31,20 @@ namespace FlaxEngine.Interop
#endif
public static class NativeToManaged
{
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target;
public static object ConvertToManaged(IntPtr unmanaged)
{
if (unmanaged == IntPtr.Zero)
return null;
object managed = ManagedHandle.FromIntPtr(unmanaged).Target;
if (managed is ManagedArray managedArray)
{
var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal);
managed = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType);
managedArrayHandle.Free();
}
return managed;
}
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged)
@@ -40,6 +53,52 @@ namespace FlaxEngine.Interop
}
}
#if FLAX_EDITOR
[HideInEditor]
#endif
public struct ManagedToNativeState
{
ManagedArray managedArray;
IntPtr handle;
public void FromManaged(object managed)
{
if (managed == null)
return;
if (managed is Array arr)
{
var type = managed.GetType();
var elementType = type.GetElementType();
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
{
// Use pooled managed array wrapper to be passed around as handle to it
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr);
handle = ManagedHandle.ToIntPtr(tmp);
}
else
{
// Convert array contents to be properly accessed by the native code (as GCHandles array)
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray));
managedArray = null; // It's not pooled
}
}
else
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
}
public IntPtr ToUnmanaged()
{
return handle;
}
public void Free()
{
managedArray?.FreePooled();
}
}
#if FLAX_EDITOR
[HideInEditor]
#endif
@@ -50,12 +109,6 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged)
{
// This is a weak handle, no need to free it
/*
if (unmanaged == IntPtr.Zero)
return;
ManagedHandle.FromIntPtr(unmanaged).Free();
*/
}
}
@@ -342,6 +395,7 @@ namespace FlaxEngine.Interop
{
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
public static void Free(IntPtr unmanaged)
{
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
@@ -614,6 +668,7 @@ namespace FlaxEngine.Interop
{
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
public static void Free(IntPtr unmanaged)
{
//ManagedString.Free(unmanaged); // No need to free weak handles

View File

@@ -1459,8 +1459,8 @@ namespace Flax.Build.Bindings
public static class NativeToManaged
{
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.NativeToManaged.Free(unmanaged);
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {}
}
#if FLAX_EDITOR
[HideInEditor]
@@ -1468,8 +1468,8 @@ namespace Flax.Build.Bindings
public static class ManagedToNative
{
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged);
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {}
}
#if FLAX_EDITOR
[HideInEditor]