diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp
index 1f119f9af..9c6554e9e 100644
--- a/Source/Editor/Scripting/ScriptsBuilder.cpp
+++ b/Source/Editor/Scripting/ScriptsBuilder.cpp
@@ -246,6 +246,8 @@ bool ScriptsBuilder::RunBuildTool(const StringView& args, const StringView& work
// Call build tool
const int32 result = Platform::RunProcess(StringView(*cmdLine, cmdLine.Length()), workingDir);
+ if (result != 0)
+ LOG(Error, "Failed to run build tool, result: {0:x}", (uint32)result);
return result != 0;
}
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 841113c9b..4c06527eb 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -12,15 +12,11 @@ using System.Runtime.CompilerServices;
using FlaxEngine.Assertions;
using FlaxEngine.Utilities;
using System.Runtime.InteropServices.Marshalling;
-using System.Reflection.Metadata;
-using System.Reflection.PortableExecutable;
using FlaxEngine.Visject;
using System.Diagnostics;
using System.Collections;
-using FlaxEditor.Content;
-
-#pragma warning disable CS1591
-#pragma warning disable CS8632
+using System.Buffers;
+using System.Collections.Concurrent;
[assembly: DisableRuntimeMarshalling]
@@ -198,48 +194,88 @@ namespace FlaxEngine
#endregion
#region Wrappers
+
///
- /// Wrapper for managed arrays that needs to be pinned.
+ /// Wrapper for managed arrays which are passed to unmanaged code.
///
- internal class ManagedArray
+ internal unsafe class ManagedArray
{
- internal Array array = null;
- internal GCHandle handle;
- internal int elementSize;
+ private Array array;
+ private GCHandle pinnedArrayHandle;
+ private IntPtr unmanagedData;
+ private int elementSize;
+ private int length;
internal ManagedArray(Array arr)
{
- handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
- elementSize = Marshal.SizeOf(arr.GetType().GetElementType());
array = arr;
+ unmanagedData = IntPtr.Zero;
+ length = arr.Length;
+ elementSize = Marshal.SizeOf(arr.GetType().GetElementType());
+ }
+
+ private ManagedArray(void* ptr, int length, int elementSize)
+ {
+ array = null;
+ this.unmanagedData = new IntPtr(ptr);
+ this.length = length;
+ this.elementSize = elementSize;
}
~ManagedArray()
{
- if (array != null)
+ if (unmanagedData != IntPtr.Zero || array != null)
Release();
}
internal void Release()
{
- handle.Free();
+ GC.SuppressFinalize(this);
+ if (pinnedArrayHandle.IsAllocated)
+ pinnedArrayHandle.Free();
array = null;
+ if (unmanagedData != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(unmanagedData);
+ unmanagedData = IntPtr.Zero;
+ }
}
- internal IntPtr PointerToPinnedArray => Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
-
- internal int Length => array.Length;
-
- internal static ManagedArray Get(ref T[] arr)
+ internal IntPtr PointerToPinnedArray
{
- return new ManagedArray(arr);
+ get
+ {
+ if (array != null)
+ {
+ // Pin the array when it's accessed for the first time
+ if (!pinnedArrayHandle.IsAllocated)
+ pinnedArrayHandle = GCHandle.Alloc(array);
+ return Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
+ }
+ else
+ return unmanagedData;
+ }
}
- internal static ManagedArray Get(Array arr)
+ internal void SetValue(IntPtr value, int index)
{
- ManagedArray managedArray = new ManagedArray(arr);
- return managedArray;
+ if (array != null)
+ array.SetValue(value, index);
+ else
+ GetSpan()[index] = value;
}
+
+ internal int Length => length;
+
+ internal int ElementSize => elementSize;
+
+ internal Span GetSpan() where T : struct => array != null ? new Span((T[]) array) : new Span(unmanagedData.ToPointer(), length);
+
+ internal T[] GetArray() where T : struct => array != null ? (T[])array : new Span(unmanagedData.ToPointer(), length).ToArray();
+
+ internal static ManagedArray Get(Array arr) => new ManagedArray(arr);
+
+ internal static ManagedArray Get(T* ptr, int length) where T : unmanaged => new ManagedArray(ptr, length, Unsafe.SizeOf());
}
internal static class ManagedString
@@ -253,22 +289,10 @@ namespace FlaxEngine
return IntPtr.Zero;
else if (str == string.Empty)
return GCHandle.ToIntPtr(EmptyStringHandle);
-#if false
- // HACK: Pin the string and pass the address to it including the length, no marshalling required
- GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);
- IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int);
- pinnedStrings.TryAdd(ptr, handle);
- return ptr;
-#endif
+ Assert.IsTrue(str.Length > 0);
-#if false
- // HACK: Return address to the allocated string structure which includes the string length.
- // We assume the string content is copied in native side before GC frees it.
- IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2);
-#endif
- IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak));
- return ptr;
+ return GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak));
}
[System.Diagnostics.DebuggerStepThrough]
@@ -277,7 +301,7 @@ namespace FlaxEngine
if (ptr == IntPtr.Zero)
return null;
- return (string)GCHandle.FromIntPtr(ptr).Target;
+ return Unsafe.As(GCHandle.FromIntPtr(ptr).Target);
}
[System.Diagnostics.DebuggerStepThrough]
@@ -294,7 +318,7 @@ namespace FlaxEngine
}
}
- #endregion
+#endregion
#region Marshallers
@@ -356,12 +380,12 @@ namespace FlaxEngine
}
}
- [CustomMarshaller(typeof(System.Type), MarshalMode.Default, typeof(SystemTypeMarshaller))]
+ [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))]
internal static class SystemTypeMarshaller
{
- internal static System.Type ConvertToManaged(IntPtr unmanaged) => (System.Type)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static Type ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged));
- internal static IntPtr ConvertToUnmanaged(System.Type managed)
+ internal static IntPtr ConvertToUnmanaged(Type managed)
{
if (managed == null)
return IntPtr.Zero;
@@ -379,7 +403,7 @@ namespace FlaxEngine
[CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))]
internal static class ExceptionMarshaller
{
- internal static Exception ConvertToManaged(IntPtr unmanaged) => (Exception)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static Exception ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged));
internal static IntPtr ConvertToUnmanaged(Exception managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
}
@@ -394,7 +418,7 @@ namespace FlaxEngine
{
public static class NativeToManaged
{
- public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (FlaxEngine.Object)GCHandle.FromIntPtr(unmanaged).Target : null;
+ public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(GCHandle.FromIntPtr(unmanaged).Target) : null;
}
public static class ManagedToNative
{
@@ -405,17 +429,44 @@ namespace FlaxEngine
[CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))]
internal static class CultureInfoMarshaller
{
- internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => (CultureInfo)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => Unsafe.As(GCHandleMarshaller.ConvertToManaged(unmanaged));
internal static IntPtr ConvertToUnmanaged(CultureInfo managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
}
- [CustomMarshaller(typeof(Array), MarshalMode.Default, typeof(SystemArrayMarshaller))]
+ [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedIn, typeof(SystemArrayMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))]
internal static class SystemArrayMarshaller
{
- internal static Array ConvertToManaged(IntPtr unmanaged) => (Array)GCHandleMarshaller.ConvertToManaged(unmanaged);
- internal static IntPtr ConvertToUnmanaged(Array managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
- internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
+ public struct ManagedToNative
+ {
+ ManagedArray managedArray;
+ GCHandle handle;
+
+ public void FromManaged(Array managed)
+ {
+ if (managed != null)
+ managedArray = ManagedArray.Get(managed);
+ }
+
+ public IntPtr ToUnmanaged()
+ {
+ if (managedArray == null)
+ return IntPtr.Zero;
+
+ handle = GCHandle.Alloc(managedArray);
+ return GCHandle.ToIntPtr(handle);
+ }
+
+ public void Free()
+ {
+ if (managedArray != null)
+ {
+ handle.Free();
+ managedArray.Release();
+ }
+ }
+ }
}
[CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))]
@@ -444,17 +495,31 @@ namespace FlaxEngine
{
Dictionary managed;
IntPtr unmanaged;
- public void FromManaged(Dictionary managed) { this.managed = managed; }
- public IntPtr ToUnmanaged() { unmanaged = DictionaryMarshaller.ToNative(managed); return unmanaged; }
- public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; }
- public Dictionary ToManaged() { managed = DictionaryMarshaller.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; }
+
+ public void FromManaged(Dictionary managed) => this.managed = managed;
+
+ public IntPtr ToUnmanaged()
+ {
+ unmanaged = DictionaryMarshaller.ToNative(managed);
+ return unmanaged;
+ }
+
+ public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged;
+
+ public Dictionary ToManaged()
+ {
+ managed = DictionaryMarshaller.ToManaged(unmanaged);
+ unmanaged = IntPtr.Zero;
+ return managed;
+ }
+
public void Free() => DictionaryMarshaller.Free(unmanaged);
}
- public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null;
+ public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(GCHandle.FromIntPtr(unmanaged).Target) : null;
public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
- public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null;
+ public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As>(GCHandle.FromIntPtr(unmanaged).Target) : null;
public static IntPtr ToNative(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
public static void Free(IntPtr unmanaged)
{
@@ -473,8 +538,7 @@ namespace FlaxEngine
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(ArrayMarshaller<,>.Bidirectional))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(ArrayMarshaller<,>))]
[ContiguousCollectionMarshaller]
- internal static unsafe class ArrayMarshaller
- where TUnmanagedElement : unmanaged
+ internal static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged
{
public static class NativeToManaged
{
@@ -483,20 +547,18 @@ namespace FlaxEngine
if (unmanaged is null)
return null;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
return new T[numElements];
}
- internal static Span GetManagedValuesDestination(T[]? managed)
- => managed;
+ internal static Span GetManagedValuesDestination(T[]? managed) => managed;
- internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
+ internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
{
- if (unmanagedValue == null)
+ if (unmanaged == null)
return ReadOnlySpan.Empty;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target;
- return new ReadOnlySpan(array.array as TUnmanagedElement[]);
+ ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ return managedArray.GetSpan();
}
internal static void Free(TUnmanagedElement* unmanaged)
@@ -505,7 +567,7 @@ namespace FlaxEngine
return;
GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
- ((ManagedArray)handle.Target).Release();
+ (Unsafe.As(handle.Target)).Release();
handle.Free();
}
@@ -514,10 +576,11 @@ namespace FlaxEngine
if (unmanaged == null)
return Span.Empty;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
- return new Span(array.array as TUnmanagedElement[]);
+ ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ return managedArray.GetSpan();
}
}
+
public static class ManagedToNative
{
internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
@@ -530,22 +593,21 @@ namespace FlaxEngine
numElements = managed.Length;
- ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]);
- var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array));
+ ManagedArray managedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length);
+ var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray));
return (TUnmanagedElement*)ptr;
}
- internal static ReadOnlySpan GetManagedValuesSource(T[]? managed)
- => managed;
+ internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed;
internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged == null)
return Span.Empty;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
- return new Span(array.array as TUnmanagedElement[]);
+ ManagedArray managedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ return managedArray.GetSpan();
}
internal static void Free(TUnmanagedElement* unmanaged)
@@ -554,10 +616,11 @@ namespace FlaxEngine
return;
GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
- ((ManagedArray)handle.Target).Release();
+ (Unsafe.As(handle.Target)).Release();
handle.Free();
}
}
+
public struct Bidirectional
{
T[] managedArray;
@@ -570,30 +633,27 @@ namespace FlaxEngine
return;
managedArray = managed;
- unmanagedArray = ManagedArray.Get(new TUnmanagedElement[managed.Length]);
+ unmanagedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length);
handle = GCHandle.Alloc(unmanagedArray);
}
- public ReadOnlySpan GetManagedValuesSource()
- => managedArray;
+ public ReadOnlySpan GetManagedValuesSource() => managedArray;
public Span GetUnmanagedValuesDestination()
{
if (unmanagedArray == null)
return Span.Empty;
- return new Span(unmanagedArray.array as TUnmanagedElement[]);
+ return unmanagedArray.GetSpan();
}
- public TUnmanagedElement* ToUnmanaged()
- {
- return (TUnmanagedElement*)GCHandle.ToIntPtr(handle);
- }
+ public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)GCHandle.ToIntPtr(handle);
- public void FromUnmanaged(TUnmanagedElement* value)
+ public void FromUnmanaged(TUnmanagedElement* unmanaged)
{
- ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(value)).Target;
- managedArray = new T[arr.Length];
+ ManagedArray arr = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ if (managedArray == null || managedArray.Length != arr.Length)
+ managedArray = new T[arr.Length];
}
public ReadOnlySpan GetUnmanagedValuesSource(int numElements)
@@ -601,14 +661,12 @@ namespace FlaxEngine
if (unmanagedArray == null)
return ReadOnlySpan.Empty;
- return new ReadOnlySpan(unmanagedArray.array as TUnmanagedElement[]);
+ return unmanagedArray.GetSpan();
}
- public Span GetManagedValuesDestination(int numElements)
- => managedArray;
+ public Span GetManagedValuesDestination(int numElements) => managedArray;
- public T[] ToManaged()
- => managedArray;
+ public T[] ToManaged() => managedArray;
public void Free()
{
@@ -627,43 +685,34 @@ namespace FlaxEngine
numElements = managed.Length;
- ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]);
- var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array));
+ ManagedArray managedArray = ManagedArray.Get((TUnmanagedElement*)Marshal.AllocHGlobal(sizeof(TUnmanagedElement) * managed.Length), managed.Length);
+ IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray));
- return (TUnmanagedElement*)ptr;
+ return (TUnmanagedElement*)handle;
}
- internal static ReadOnlySpan GetManagedValuesSource(T[]? managed)
- => managed;
+ internal static ReadOnlySpan GetManagedValuesSource(T[]? managed) => managed;
internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
{
if (unmanaged == null)
return Span.Empty;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
- return new Span(array.array as TUnmanagedElement[]);
+ ManagedArray unmanagedArray = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ return unmanagedArray.GetSpan();
}
- internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
+ internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements];
+
+ internal static Span GetManagedValuesDestination(T[]? managed) => managed;
+
+ internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
{
- if (unmanaged is null)
- return null;
-
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
- return new T[numElements];
- }
-
- internal static Span GetManagedValuesDestination(T[]? managed)
- => managed;
-
- internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
- {
- if (unmanagedValue == null)
+ if (unmanaged == null)
return ReadOnlySpan.Empty;
- ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target;
- return new ReadOnlySpan(array.array as TUnmanagedElement[]);
+ ManagedArray array = Unsafe.As(GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
+ return array.GetSpan();
}
internal static void Free(TUnmanagedElement* unmanaged)
@@ -672,19 +721,9 @@ namespace FlaxEngine
return;
GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
- ((ManagedArray)handle.Target).Release();
+ Unsafe.As(handle.Target).Release();
handle.Free();
}
-
- internal static ref T GetPinnableReference(T[]? array)
- {
- if (array is null)
- return ref Unsafe.NullRef();
-
- ManagedArray managedArray = ManagedArray.Get(array);
- var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray));
- return ref Unsafe.AsRef(ptr.ToPointer());
- }
}
[CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))]
@@ -703,41 +742,45 @@ namespace FlaxEngine
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
}
+
public static class ManagedToNative
{
public static unsafe IntPtr ConvertToUnmanaged(string managed)
{
if (managed == null)
return IntPtr.Zero;
-#if false
- // HACK: Pin the string and pass the address to it including the length, no marshalling required
- GCHandle handle = GCHandle.Alloc(managed, GCHandleType.Pinned);
- IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int);
- pinnedStrings.TryAdd(ptr, handle);
- return ptr;
-#endif
-
-#if false
- // HACK: Return address to the allocated string structure which includes the string length.
- // We assume the string content is copied in native side before GC frees it.
- IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref managed)) + sizeof(int) * 2);
-#endif
- IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Pinned));
- return ptr;
+ return GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Weak));
}
+
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
}
+
public struct Bidirectional
{
string managed;
IntPtr unmanaged;
- public void FromManaged(string managed) { this.managed = managed; }
- public IntPtr ToUnmanaged() { unmanaged = ManagedString.ToNative(managed); return unmanaged; }
- public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; }
- public string ToManaged() { managed = ManagedString.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; }
+
+ public void FromManaged(string managed) => this.managed = managed;
+
+ public IntPtr ToUnmanaged()
+ {
+ unmanaged = ManagedString.ToNative(managed);
+ return unmanaged;
+ }
+
+ public void FromUnmanaged(IntPtr unmanaged) => this.unmanaged = unmanaged;
+
+ public string ToManaged()
+ {
+ managed = ManagedString.ToManaged(unmanaged);
+ unmanaged = IntPtr.Zero;
+ return managed;
+ }
+
public void Free() => ManagedString.Free(unmanaged);
}
+
internal static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
internal static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed);
internal static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
@@ -753,23 +796,26 @@ namespace FlaxEngine
///
internal unsafe static partial class NativeInterop
{
+ ///
+ /// Enables first-chance exception handling in invoked methods while debugger is attached.
+ ///
+ public static bool CatchExceptions = true;
+
internal static Dictionary AssemblyLocations = new Dictionary();
private static bool firstAssemblyLoaded = false;
-
- private static Dictionary typeCache = new Dictionary();
- private static Dictionary marshallableStructCache = new Dictionary();
+ private static Dictionary typeCache = new Dictionary();
private static IntPtr boolTruePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)1, GCHandleType.Pinned));
private static IntPtr boolFalsePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)0, GCHandleType.Pinned));
private static List methodHandles = new();
private static List methodHandlesCollectible = new();
- private static Dictionary cachedDelegates = new Dictionary();
- private static Dictionary cachedDelegatesCollectible = new Dictionary();
- private static Dictionary typeHandleCache = new Dictionary();
- private static Dictionary typeHandleCacheCollectible = new Dictionary();
+ private static ConcurrentDictionary cachedDelegates = new ConcurrentDictionary();
+ private static ConcurrentDictionary cachedDelegatesCollectible = new ConcurrentDictionary();
+ private static Dictionary typeHandleCache = new Dictionary();
+ private static Dictionary typeHandleCacheCollectible = new Dictionary();
private static List fieldHandleCache = new();
private static List fieldHandleCacheCollectible = new();
private static Dictionary