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; }
}
}