From ba49b74b73c4d8fc8441be5d235cfb77e62aae9a Mon Sep 17 00:00:00 2001 From: David Hall Date: Wed, 13 Nov 2019 23:02:11 -0500 Subject: [PATCH] Removed IMarshalDirective and created IVanaraMarshaler with supporting attribute and helper class. --- Core/InteropServices/IntPtrConverter.cs | 25 +----- Core/InteropServices/VanaraMarshaler.cs | 145 ++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 Core/InteropServices/VanaraMarshaler.cs diff --git a/Core/InteropServices/IntPtrConverter.cs b/Core/InteropServices/IntPtrConverter.cs index 4a59c987..e04f3790 100644 --- a/Core/InteropServices/IntPtrConverter.cs +++ b/Core/InteropServices/IntPtrConverter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; @@ -8,24 +9,6 @@ using Vanara.PInvoke; namespace Vanara.InteropServices { - /// Delegate that marshals native memory to an object. See . - /// The pointer to a block of memory. - /// The size of the allocated memory block. - /// An instance of the object pointed to in memory. - public delegate object MarshalDirectiveActivator(IntPtr ptr, SizeT allocatedBytes); - - /// Supports methods that allow for marshaling to and from native memory. - public interface IMarshalDirective - { - /// Returns a method that creates a new instance of this type from native memory. - /// A delegate n object of the current type that matches - MarshalDirectiveActivator GetActivator(); - - /// Converts the current instance to its native equivalent. - /// Allocated memory with the binary representation of this instance. - SafeAllocatedMemoryHandle ToNative(); - } - /// Functions to safely convert a memory pointer to a type. public static class IntPtrConverter { @@ -65,11 +48,9 @@ namespace Vanara.InteropServices switch (typeCode) { case TypeCode.Object: - if (typeof(IMarshalDirective).IsAssignableFrom(destType)) + if (VanaraMarshaler.CanMarshal(destType, out var marshaler)) { - var f = ((IMarshalDirective)Activator.CreateInstance(destType)).GetActivator(); - if (f != null) - return f(ptr, sz); + return marshaler.MarshalNativeToManaged(ptr, sz); } if (typeof(ISerializable).IsAssignableFrom(destType)) { diff --git a/Core/InteropServices/VanaraMarshaler.cs b/Core/InteropServices/VanaraMarshaler.cs new file mode 100644 index 00000000..9d2f6de8 --- /dev/null +++ b/Core/InteropServices/VanaraMarshaler.cs @@ -0,0 +1,145 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using Vanara.Extensions; +using Vanara.PInvoke; + +namespace Vanara.InteropServices +{ + /// Smarter custom marshaler. + public interface IVanaraMarshaler + { + /// Gets the size of the native data. + /// + /// The size, in bytes, of the base object in memory. This should return the equivalent of the sizeof(X) function in C/C++. + /// + SizeT GetNativeSize(); + + /// Marshals the managed object to its native, in-memory, value. + /// The managed object to marshal. + /// The self-destroying handle to the binary representation. + SafeAllocatedMemoryHandle MarshalManagedToNative(object managedObject); + + /// Marshals the native memory to a managed object. + /// The pointer to the native data. + /// The number of allocated bytes. + /// The type instance. + object MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes); + } + + [VanaraMarshaler(typeof(TestMarshal))] + internal struct TestMarshal : IVanaraMarshaler + { + public bool bVal; + public uint[] uArray; + + SizeT IVanaraMarshaler.GetNativeSize() => 12; + + SafeAllocatedMemoryHandle IVanaraMarshaler.MarshalManagedToNative(object managedObject) + { + if (managedObject is null) return SafeHGlobalHandle.Null; + if (!(managedObject is TestMarshal t)) + throw new ArgumentException($"Object must be a {nameof(TestMarshal)} instance.", nameof(managedObject)); + var mem = new SafeHGlobalHandle(12); + mem.Write(t.bVal ? 1U : 0U); + mem.Write(t.uArray?.Length ?? 0); + mem.Write(t.uArray, true, 8); + return mem; + } + + object IVanaraMarshaler.MarshalNativeToManaged(IntPtr pNativeData, SizeT allocatedBytes) + { + var ret = default(TestMarshal); + if (pNativeData != IntPtr.Zero) + { + using var str = new NativeMemoryStream(pNativeData, allocatedBytes); + ret.bVal = str.Read() != 0; + var len = str.Read(); + ret.uArray = str.ReadArray(len, false).ToArray(); + } + return ret; + } + } + + /// Provides methods to assist with custom marshaling. + public static class VanaraMarshaler + { + /// Determines whether a type can be marshaled. + /// The type to check. + /// On success, the marshaler instance. + /// if this type can marshaled; otherwise, . + public static bool CanMarshal(Type t, out IVanaraMarshaler marshaler) + { + var vattr = t.GetCustomAttributes(true).FirstOrDefault(); + if (vattr != null) + { + marshaler = Activator.CreateInstance(vattr.MarshalType) as IVanaraMarshaler; + return marshaler != null; + } + if (typeof(IVanaraMarshaler).IsAssignableFrom(t)) + { + marshaler = Activator.CreateInstance(t) as IVanaraMarshaler; + return marshaler != null; + } + marshaler = null; + return false; + } + + /// Determines whether a type can be marshaled. + /// The type to check. + /// On success, the marshaler instance. + /// if this type can marshaled; otherwise, . + public static bool CanMarshal(out IVanaraMarshaler marshaler) => CanMarshal(typeof(T), out marshaler); + } + + /// Provides an instance that utilizes an implementation. + /// + /// The type that either implements or uses to specify a type. + /// + /// + public class VanaraCustomMarshaler : ICustomMarshaler + { + private SafeAllocatedMemoryHandle mem; + + private VanaraCustomMarshaler(string _) + { + } + + /// Gets the instance. + /// The cookie. + /// + public static ICustomMarshaler GetInstance(string cookie) => new VanaraCustomMarshaler(cookie); + + void ICustomMarshaler.CleanUpManagedData(object ManagedObj) + { + } + + void ICustomMarshaler.CleanUpNativeData(IntPtr pNativeData) => mem?.Dispose(); + + int ICustomMarshaler.GetNativeDataSize() => VanaraMarshaler.CanMarshal(out var m) ? (int)m.GetNativeSize() : -1; + + IntPtr ICustomMarshaler.MarshalManagedToNative(object ManagedObj) => VanaraMarshaler.CanMarshal(out var m) ? (mem = m.MarshalManagedToNative(ManagedObj)) : throw new InvalidOperationException("Cannot marshal this type."); + + object ICustomMarshaler.MarshalNativeToManaged(IntPtr pNativeData) => VanaraMarshaler.CanMarshal(out var m) ? m.MarshalNativeToManaged(pNativeData, SizeT.MaxValue) : throw new InvalidOperationException("Cannot marshal this type."); + } + + /// Apply this attribute to a class or structure to have all Vanara interop function process via the marshaler. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] + public class VanaraMarshalerAttribute : Attribute + { + /// Initializes a new instance of the class. + /// A type that derives from that will marshal this class or structure. + public VanaraMarshalerAttribute(Type marshalType) + { + if (marshalType is null) + throw new ArgumentNullException(nameof(marshalType)); + if (!typeof(IVanaraMarshaler).IsAssignableFrom(marshalType)) + throw new ArgumentException($"The supplied type must inherit from {nameof(IVanaraMarshaler)}.", nameof(marshalType)); + MarshalType = marshalType; + } + + /// Gets the type that will marshal this class or structure. + public Type MarshalType { get; } + } +} \ No newline at end of file