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); } /// 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) { var cookie = vattr.Cookie; marshaler = cookie is null ? Activator.CreateInstance(vattr.MarshalType) as IVanaraMarshaler : Activator.CreateInstance(vattr.MarshalType, cookie) 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 readonly string cookie; private VanaraCustomMarshaler(string cookie) => this.cookie = cookie; /// 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. /// The cookie value to pass to the constructor. /// marshalType /// The supplied type must inherit from {nameof(IVanaraMarshaler)}. - marshalType public VanaraMarshalerAttribute(Type marshalType, string cookie = null) { 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; Cookie = cookie; } /// Gets the cookie value, that if not , will get passed to the constructor of . public string Cookie { get; } /// Gets the type that will marshal this class or structure. public Type MarshalType { get; } } }