diff --git a/Core/Extensions/InteropExtensions.cs b/Core/Extensions/InteropExtensions.cs index 1bce8224..3be5d7a4 100644 --- a/Core/Extensions/InteropExtensions.cs +++ b/Core/Extensions/InteropExtensions.cs @@ -279,14 +279,21 @@ namespace Vanara.Extensions /// /// The bytes allocated by the method. /// Number of bytes preceding the trailing strings. + /// + /// The function used to lock memory before assignment. If , the result from will + /// be used. + /// + /// The optional function to unlock memory after assignment. /// A pointer to the memory allocated by . - public static IntPtr MarshalToPtr(this T value, Func memAlloc, out int bytesAllocated, int prefixBytes = 0) + public static IntPtr MarshalToPtr(this T value, Func memAlloc, out int bytesAllocated, int prefixBytes = 0, Func memLock = null, Action memUnlock = null) { + memLock ??= Passthrough; if (VanaraMarshaler.CanMarshal(typeof(T), out var marshaler)) { using var mem = marshaler.MarshalManagedToNative(value); var ret = memAlloc(bytesAllocated = mem.Size + prefixBytes); - mem.DangerousGetHandle().CopyTo(ret.Offset(prefixBytes), mem.Size); + mem.DangerousGetHandle().CopyTo(memLock(ret).Offset(prefixBytes), mem.Size); + memUnlock?.Invoke(ret); return ret; } else @@ -294,11 +301,14 @@ namespace Vanara.Extensions var newVal = TrueValue(value, out bytesAllocated); bytesAllocated += prefixBytes; var ret = memAlloc(bytesAllocated); - Write(ret, newVal, prefixBytes, bytesAllocated); + Write(memLock(ret), newVal, prefixBytes, bytesAllocated); + memUnlock?.Invoke(ret); return ret; } } + private static IntPtr Passthrough(IntPtr p) => p; + /// /// Marshals data from a managed list of specified type to an unmanaged block of memory allocated by the method. /// @@ -312,12 +322,18 @@ namespace Vanara.Extensions /// /// The bytes allocated by the method. /// Number of bytes preceding the trailing strings. + /// + /// The function used to lock memory before assignment. If , the result from will + /// be used. + /// + /// The optional function to unlock memory after assignment. /// Pointer to the allocated native (unmanaged) array of items stored. /// Structure layout is not sequential or explicit. - public static IntPtr MarshalToPtr(this IEnumerable items, Func memAlloc, out int bytesAllocated, int prefixBytes = 0) + public static IntPtr MarshalToPtr(this IEnumerable items, Func memAlloc, out int bytesAllocated, int prefixBytes = 0, Func memLock = null, Action memUnlock = null) { if (!typeof(T).IsMarshalable()) throw new ArgumentException(@"Structure layout is not sequential or explicit."); + memLock ??= Passthrough; bytesAllocated = prefixBytes; var count = items?.Count() ?? 0; if (count == 0) return memAlloc(bytesAllocated); @@ -325,7 +341,9 @@ namespace Vanara.Extensions var sz = Marshal.SizeOf(typeof(T)); bytesAllocated += sz * count; var result = memAlloc(bytesAllocated); - result.Write(items, prefixBytes, bytesAllocated); + memLock(result).Write(items, prefixBytes, bytesAllocated); + memUnlock?.Invoke(result); + return result; } @@ -341,10 +359,15 @@ namespace Vanara.Extensions /// /// The bytes allocated by the method. /// Number of bytes preceding the trailing strings. + /// + /// The function used to lock memory before assignment. If , the result from will + /// be used. + /// + /// The optional function to unlock memory after assignment. /// Pointer to the allocated native (unmanaged) array of items stored. /// Structure layout is not sequential or explicit. - public static IntPtr MarshalToPtr(this T[] items, Func memAlloc, out int bytesAllocated, int prefixBytes = 0) => - MarshalToPtr(items.Cast(), memAlloc, out bytesAllocated, prefixBytes); + public static IntPtr MarshalToPtr(this T[] items, Func memAlloc, out int bytesAllocated, int prefixBytes = 0, Func memLock = null, Action memUnlock = null) => + MarshalToPtr(items.Cast(), memAlloc, out bytesAllocated, prefixBytes, memLock, memUnlock); /// /// Marshals data from a managed list of strings to an unmanaged block of memory allocated by the method. @@ -357,18 +380,26 @@ namespace Vanara.Extensions /// The bytes allocated by the method. /// The character set to use for the strings. /// Number of bytes preceding the trailing strings. + /// + /// The function used to lock memory before assignment. If , the result from will + /// be used. + /// + /// The optional function to unlock memory after assignment. /// /// Pointer to the allocated native (unmanaged) array of strings stored using the model and the character /// set defined by . /// - public static IntPtr MarshalToPtr(this IEnumerable values, StringListPackMethod packing, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0) + public static IntPtr MarshalToPtr(this IEnumerable values, StringListPackMethod packing, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0, Func memLock = null, Action memUnlock = null) { + memLock ??= Passthrough; + // Bail early if empty if (values is null || !values.Any()) { bytesAllocated = prefixBytes + (packing == StringListPackMethod.Concatenated ? StringHelper.GetCharSize(charSet) : IntPtr.Size); var ret = memAlloc(bytesAllocated); - ret.FillMemory(0, bytesAllocated); + memLock(ret).FillMemory(0, bytesAllocated); + memUnlock?.Invoke(ret); return ret; } @@ -396,7 +427,8 @@ namespace Vanara.Extensions // Copy to newly allocated memory using memAlloc bytesAllocated = (int)ms.Length; var ret = memAlloc(bytesAllocated); - ms.Pointer.CopyTo(ret, bytesAllocated); + ms.Pointer.CopyTo(memLock(ret), bytesAllocated); + memUnlock?.Invoke(ret); return ret; } } @@ -412,12 +444,17 @@ namespace Vanara.Extensions /// The bytes allocated by the method. /// The character set to use for the strings. /// Number of bytes preceding the trailing strings. + /// + /// The function used to lock memory before assignment. If , the result from will + /// be used. + /// + /// The optional function to unlock memory after assignment. /// /// Pointer to the allocated native (unmanaged) array of strings stored using the model and the character /// set defined by . /// - public static IntPtr MarshalToPtr(this string[] values, StringListPackMethod packing, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => - MarshalToPtr((IEnumerable)values, packing, memAlloc, out bytesAllocated, charSet, prefixBytes); + public static IntPtr MarshalToPtr(this string[] values, StringListPackMethod packing, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0, Func memLock = null, Action memUnlock = null) => + MarshalToPtr((IEnumerable)values, packing, memAlloc, out bytesAllocated, charSet, prefixBytes, memLock, memUnlock); /// Adds an offset to the value of a pointer. /// The pointer to add the offset to.