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