2019-11-13 23:02:11 -05:00
using System ;
using System.Linq ;
using System.Runtime.InteropServices ;
using Vanara.Extensions ;
using Vanara.PInvoke ;
namespace Vanara.InteropServices
{
/// <summary>Smarter custom marshaler.</summary>
public interface IVanaraMarshaler
{
/// <summary>Gets the size of the native data.</summary>
/// <returns>
/// The size, in bytes, of the base object in memory. This should return the equivalent of the sizeof(X) function in C/C++.
/// </returns>
SizeT GetNativeSize ( ) ;
/// <summary>Marshals the managed object to its native, in-memory, value.</summary>
/// <param name="managedObject">The managed object to marshal.</param>
/// <returns>The self-destroying handle to the binary representation.</returns>
SafeAllocatedMemoryHandle MarshalManagedToNative ( object managedObject ) ;
/// <summary>Marshals the native memory to a managed object.</summary>
/// <param name="pNativeData">The pointer to the native data.</param>
/// <param name="allocatedBytes">The number of allocated bytes.</param>
/// <returns>The type instance.</returns>
object MarshalNativeToManaged ( IntPtr pNativeData , SizeT allocatedBytes ) ;
}
/// <summary>Provides methods to assist with custom marshaling.</summary>
public static class VanaraMarshaler
{
/// <summary>Determines whether a type can be marshaled.</summary>
/// <param name="t">The type to check.</param>
/// <param name="marshaler">On success, the marshaler instance.</param>
/// <returns><see langword="true"/> if this type can marshaled; otherwise, <see langword="false"/>.</returns>
public static bool CanMarshal ( Type t , out IVanaraMarshaler marshaler )
{
var vattr = t . GetCustomAttributes < VanaraMarshalerAttribute > ( true ) . FirstOrDefault ( ) ;
if ( vattr ! = null )
{
2019-11-16 17:48:44 -05:00
var cookie = vattr . Cookie ;
marshaler = cookie is null ?
Activator . CreateInstance ( vattr . MarshalType ) as IVanaraMarshaler :
Activator . CreateInstance ( vattr . MarshalType , cookie ) as IVanaraMarshaler ;
2019-11-13 23:02:11 -05:00
return marshaler ! = null ;
}
if ( typeof ( IVanaraMarshaler ) . IsAssignableFrom ( t ) )
{
marshaler = Activator . CreateInstance ( t ) as IVanaraMarshaler ;
return marshaler ! = null ;
}
marshaler = null ;
return false ;
}
/// <summary>Determines whether a type can be marshaled.</summary>
/// <typeparam name="T">The type to check.</typeparam>
/// <param name="marshaler">On success, the marshaler instance.</param>
/// <returns><see langword="true"/> if this type can marshaled; otherwise, <see langword="false"/>.</returns>
public static bool CanMarshal < T > ( out IVanaraMarshaler marshaler ) = > CanMarshal ( typeof ( T ) , out marshaler ) ;
}
/// <summary>Provides an <see cref="ICustomMarshaler"/> instance that utilizes an <see cref="IVanaraMarshaler"/> implementation.</summary>
/// <typeparam name="T">
/// The type that either implements <see cref="IVanaraMarshaler"/> or uses <see cref="VanaraMarshalerAttribute"/> to specify a type.
/// </typeparam>
/// <seealso cref="System.Runtime.InteropServices.ICustomMarshaler"/>
public class VanaraCustomMarshaler < T > : ICustomMarshaler
{
private SafeAllocatedMemoryHandle mem ;
2019-11-16 17:48:44 -05:00
private readonly string cookie ;
2019-11-13 23:02:11 -05:00
2019-11-16 17:48:44 -05:00
private VanaraCustomMarshaler ( string cookie ) = > this . cookie = cookie ;
2019-11-13 23:02:11 -05:00
/// <summary>Gets the instance.</summary>
/// <param name="cookie">The cookie.</param>
/// <returns></returns>
public static ICustomMarshaler GetInstance ( string cookie ) = > new VanaraCustomMarshaler < T > ( cookie ) ;
void ICustomMarshaler . CleanUpManagedData ( object ManagedObj )
{
}
void ICustomMarshaler . CleanUpNativeData ( IntPtr pNativeData ) = > mem ? . Dispose ( ) ;
int ICustomMarshaler . GetNativeDataSize ( ) = > VanaraMarshaler . CanMarshal < T > ( out var m ) ? ( int ) m . GetNativeSize ( ) : - 1 ;
IntPtr ICustomMarshaler . MarshalManagedToNative ( object ManagedObj ) = > VanaraMarshaler . CanMarshal < T > ( out var m ) ? ( mem = m . MarshalManagedToNative ( ManagedObj ) ) : throw new InvalidOperationException ( "Cannot marshal this type." ) ;
object ICustomMarshaler . MarshalNativeToManaged ( IntPtr pNativeData ) = > VanaraMarshaler . CanMarshal < T > ( out var m ) ? m . MarshalNativeToManaged ( pNativeData , SizeT . MaxValue ) : throw new InvalidOperationException ( "Cannot marshal this type." ) ;
}
/// <summary>Apply this attribute to a class or structure to have all Vanara interop function process via the marshaler.</summary>
/// <seealso cref="System.Attribute"/>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public class VanaraMarshalerAttribute : Attribute
{
/// <summary>Initializes a new instance of the <see cref="VanaraMarshalerAttribute"/> class.</summary>
/// <param name="marshalType">A type that derives from <see cref="IVanaraMarshaler"/> that will marshal this class or structure.</param>
2019-11-16 17:48:44 -05:00
/// <param name="cookie">The cookie value to pass to the constructor.</param>
/// <exception cref="ArgumentNullException">marshalType</exception>
/// <exception cref="ArgumentException">The supplied type must inherit from {nameof(IVanaraMarshaler)}. - marshalType</exception>
public VanaraMarshalerAttribute ( Type marshalType , string cookie = null )
2019-11-13 23:02:11 -05:00
{
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 ;
2019-11-16 17:48:44 -05:00
Cookie = cookie ;
2019-11-13 23:02:11 -05:00
}
2019-11-16 17:48:44 -05:00
/// <summary>Gets the cookie value, that if not <see langword="null"/>, will get passed to the constructor of <see cref="MarshalType"/>.</summary>
public string Cookie { get ; }
2019-11-13 23:02:11 -05:00
/// <summary>Gets the type that will marshal this class or structure.</summary>
public Type MarshalType { get ; }
}
}