2017-11-27 12:18:01 -05:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Runtime.InteropServices ;
2019-12-09 09:22:20 -05:00
using System.Runtime.Serialization.Formatters.Binary ;
2017-11-27 12:18:01 -05:00
using System.Security ;
using System.Security.Permissions ;
using Vanara.InteropServices ;
2019-08-17 23:13:57 -04:00
using Vanara.PInvoke ;
2020-05-15 18:37:15 -04:00
using Vanara.PInvoke.Collections ;
2017-11-27 12:18:01 -05:00
namespace Vanara.Extensions
{
/// <summary>Extension methods for System.Runtime.InteropServices.</summary>
public static partial class InteropExtensions
{
2020-09-10 12:11:58 -04:00
/// <summary>
/// Aligns the specified pointer to an adjacent memory location that can be accessed by a adding a constant and its multiples.
/// </summary>
/// <param name="ptr">The pointer to align.</param>
/// <returns>The aligned pointer. This value may be the same as <paramref name="ptr"/>.</returns>
public static IntPtr Align ( this IntPtr ptr ) = > new IntPtr ( ( ptr . ToInt64 ( ) + IntPtr . Size - 1 ) & ( ~ ( ( ( long ) IntPtr . Size ) - 1 ) ) ) ;
2020-05-15 13:40:11 -04:00
#if ALLOWSPAN
/// <summary>Returns the pointer as a <see cref="ReadOnlySpan{T}"/>.</summary>
/// <typeparam name="T">The type of items in the <see cref="ReadOnlySpan{T}"/>.</typeparam>
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements to be included in the <see cref="ReadOnlySpan{T}"/>.</param>
/// <param name="prefixBytes">Bytes to skip before starting the span.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>A <see cref="ReadOnlySpan{T}"/> that represents the memory.</returns>
/// <exception cref="System.InsufficientMemoryException"></exception>
public static unsafe ReadOnlySpan < T > AsReadOnlySpan < T > ( this IntPtr ptr , int length , int prefixBytes = 0 , SizeT allocatedBytes = default )
{
if ( ptr = = IntPtr . Zero ) return null ;
if ( length < 0 ) throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
if ( allocatedBytes > 0 & & SizeOf < T > ( ) * length + prefixBytes > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
return new ReadOnlySpan < T > ( ( ptr + prefixBytes ) . ToPointer ( ) , length ) ;
}
2020-11-10 23:35:58 -05:00
/// <summary>Gets a reference to a structure based on this allocated memory.</summary>
/// <typeparam name="T">The type of items in the <see cref="Span{T}"/>.</typeparam>
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
/// <param name="prefixBytes">Bytes to skip before starting the span.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>A referenced structure.</returns>
public static ref T AsRef < T > ( this IntPtr ptr , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
ref MemoryMarshal . GetReference ( AsSpan < T > ( ptr , 1 , prefixBytes , allocatedBytes ) ) ;
2020-05-15 13:40:11 -04:00
/// <summary>Returns the pointer as a <see cref="Span{T}"/>.</summary>
/// <typeparam name="T">The type of items in the <see cref="Span{T}"/>.</typeparam>
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements to be included in the <see cref="Span{T}"/>.</param>
/// <param name="prefixBytes">Bytes to skip before starting the span.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>A <see cref="Span{T}"/> that represents the memory.</returns>
/// <exception cref="System.InsufficientMemoryException"></exception>
public static unsafe Span < T > AsSpan < T > ( this IntPtr ptr , int length , int prefixBytes = 0 , SizeT allocatedBytes = default )
{
if ( ptr = = IntPtr . Zero ) return null ;
if ( length < 0 ) throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
if ( allocatedBytes > 0 & & SizeOf < T > ( ) * length + prefixBytes > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
return new Span < T > ( ( ptr + prefixBytes ) . ToPointer ( ) , length ) ;
}
# endif
2020-07-07 09:12:58 -04:00
/// <summary>Returns the pointer.</summary>
/// <typeparam name="T">The type of items.</typeparam>
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements to be included in the pointer.</param>
/// <param name="prefixBytes">Bytes to skip before starting the span.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>A pointer that represents the memory.</returns>
/// <exception cref="System.InsufficientMemoryException"></exception>
public static unsafe T * AsUnmanagedArrayPointer < T > ( this IntPtr ptr , int length , int prefixBytes = 0 , SizeT allocatedBytes = default ) where T : unmanaged
{
if ( ptr = = IntPtr . Zero ) return null ;
if ( length < 0 ) throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
if ( allocatedBytes > 0 & & SizeOf < T > ( ) * length + prefixBytes > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
return ( T * ) ptr . Offset ( prefixBytes ) . ToPointer ( ) ;
}
2019-04-10 14:13:28 -04:00
/// <summary>Copies the number of specified bytes from one unmanaged memory block to another.</summary>
/// <param name="ptr">The allocated memory pointer.</param>
/// <param name="dest">The allocated memory pointer to copy to.</param>
/// <param name="length">The number of bytes to copy from <paramref name="ptr"/> to <paramref name="dest"/>.</param>
2019-05-08 11:23:04 -04:00
public static void CopyTo ( this IntPtr ptr , IntPtr dest , long length ) = > CopyTo ( ptr , 0L , dest , length ) ;
/// <summary>Copies the number of specified bytes from one unmanaged memory block to another.</summary>
/// <param name="source">The allocated memory pointer.</param>
/// <param name="start">The offset from <paramref name="source"/> at which to start the copying.</param>
/// <param name="dest">The allocated memory pointer to copy to.</param>
/// <param name="length">The number of bytes to copy from <paramref name="source"/> to <paramref name="dest"/>.</param>
public static void CopyTo ( this IntPtr source , long start , IntPtr dest , long length )
2019-04-10 14:13:28 -04:00
{
2019-05-08 11:23:04 -04:00
if ( start < 0 | | length < 0 ) throw new ArgumentOutOfRangeException ( ) ;
if ( source = = IntPtr . Zero | | dest = = IntPtr . Zero ) throw new ArgumentNullException ( ) ;
unsafe
{
byte * psrc = ( byte * ) source + start , pdest = ( byte * ) dest ;
for ( long i = 0 ; i < length ; i + + , psrc + + , pdest + + )
* pdest = * psrc ;
}
2019-04-10 14:13:28 -04:00
}
2019-01-25 11:08:18 -05:00
/// <summary>
/// Fills the memory with a particular byte value. <note type="warning">This is a very dangerous function that can cause memory
2021-01-11 13:22:39 -05:00
/// access errors if the provided <paramref name="length"/> is bigger than allocated memory of if the <paramref name="ptr"/> is not
/// a valid memory pointer.</note>
2019-01-25 11:08:18 -05:00
/// </summary>
/// <param name="ptr">The allocated memory pointer.</param>
/// <param name="value">The byte value with which to fill the memory.</param>
/// <param name="length">The number of bytes to fill with the value.</param>
2019-03-25 20:34:03 -04:00
public static void FillMemory ( this IntPtr ptr , byte value , long length )
2019-01-25 11:08:18 -05:00
{
if ( ptr = = IntPtr . Zero | | length < = 0 ) return ;
// Write multiples of 8 bytes first
2019-03-27 00:03:39 -04:00
var lval = value = = 0 ? 0L : BitConverter . ToInt64 ( new [ ] { value , value , value , value , value , value , value , value } , 0 ) ;
2019-03-25 20:34:03 -04:00
for ( var ofs = 0L ; ofs < length / 8 ; ofs + + )
Marshal . WriteInt64 ( ptr . Offset ( ofs * 8 ) , 0 , lval ) ;
2019-01-25 11:08:18 -05:00
// Write remaining bytes
for ( var ofs = length - ( length % 8 ) ; ofs < length ; ofs + + )
2019-03-25 20:34:03 -04:00
Marshal . WriteByte ( ptr . Offset ( ofs ) , 0 , value ) ;
2019-01-25 11:08:18 -05:00
}
2020-05-15 18:37:15 -04:00
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="IEnumerator{T}"/>.</summary>
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static IEnumerator < T > GetEnumerator < T > ( this IntPtr ptr , int count , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
new NativeMemoryEnumerator < T > ( ptr , count , prefixBytes , allocatedBytes ) ;
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="System.Collections.IEnumerator"/>.</summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="type">Type of native structure used by the C-style array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static System . Collections . IEnumerator GetEnumerator ( this IntPtr ptr , Type type , int count , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
new UntypedNativeMemoryEnumerator ( ptr , type , count , prefixBytes , allocatedBytes ) ;
2018-11-19 23:18:50 -05:00
/// <summary>
/// Gets the length of a null terminated array of pointers. <note type="warning">This is a very dangerous function and can result in
/// memory access errors if the <paramref name="lptr"/> does not point to a null-terminated array of pointers.</note>
/// </summary>
/// <param name="lptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <returns>
/// The number of non-null pointers in the array. If <paramref name="lptr"/> is equal to IntPtr.Zero, this result is 0.
/// </returns>
public static int GetNulledPtrArrayLength ( this IntPtr lptr )
{
if ( lptr = = IntPtr . Zero ) return 0 ;
var c = 0 ;
2019-03-27 00:03:39 -04:00
while ( Marshal . ReadIntPtr ( lptr , IntPtr . Size * c + + ) ! = IntPtr . Zero ) { }
2018-11-19 23:18:50 -05:00
return c - 1 ;
}
2017-11-27 12:18:01 -05:00
/// <summary>Determines whether this type is formatted or blittable.</summary>
/// <param name="T">The type to check.</param>
2019-12-06 19:07:21 -05:00
/// <returns><see langword="true"/> if the specified type is blittable; otherwise, <see langword="false"/>.</returns>
2017-11-27 12:18:01 -05:00
public static bool IsBlittable ( this Type T )
{
2019-03-27 00:03:39 -04:00
if ( T is null ) return false ;
2017-11-27 12:18:01 -05:00
// if (T.IsArray && T.GetArrayRank() > 1) return false; // Need to find a way to exclude jagged arrays
while ( T . IsArray )
T = T . GetElementType ( ) ;
/ /
//if (T == typeof(decimal) || T.IsAbstract || T.IsAutoClass || T.IsGenericType) return false;
//if (T.IsEnum || T.IsPrimitive && T != typeof(bool) && T != typeof(char)) return true;
//try
//{
// GCHandle.Alloc(FormatterServices.GetUninitializedObject(T), GCHandleType.Pinned).Free();
// return true;
//}
//catch
//{
// return false;
//}
if ( T . IsEnum ) return true ;
try { Marshal . SizeOf ( T ) ; return true ; } catch { return false ; }
}
2019-12-06 19:07:21 -05:00
/// <summary>Determines whether this type is marshalable.</summary>
/// <param name="type">The type to check.</param>
/// <returns><see langword="true"/> if the specified type is marshalable; otherwise, <see langword="false"/>.</returns>
public static bool IsMarshalable ( this Type type )
{
2021-01-11 13:22:39 -05:00
Type t = type . IsNullable ( ) ? type . GetGenericArguments ( ) [ 0 ] : type ;
2019-12-06 19:07:21 -05:00
return t . IsSerializable | | VanaraMarshaler . CanMarshal ( t , out _ ) | | t . IsBlittable ( ) ;
}
2017-11-27 12:18:01 -05:00
/// <summary>Determines whether this type is nullable (derived from <see cref="Nullable{T}"/>).</summary>
/// <param name="type">The type to check.</param>
2019-12-06 19:07:21 -05:00
/// <returns><see langword="true"/> if the specified type is nullable; otherwise, <see langword="false"/>.</returns>
2017-11-27 12:18:01 -05:00
public static bool IsNullable ( this Type type ) = > type . IsGenericType & & type . GetGenericTypeDefinition ( ) = = typeof ( Nullable < > ) ;
2018-01-06 18:34:28 -05:00
/// <summary>Marshals an unmanaged linked list of structures to an <see cref="IEnumerable{T}"/> of that structure.</summary>
/// <typeparam name="T">Type of native structure used by the unmanaged linked list.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="next">The expression to be used to fetch the pointer to the next item in the list.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the linked list.</returns>
public static IEnumerable < T > LinkedListToIEnum < T > ( this IntPtr ptr , Func < T , IntPtr > next )
{
2021-01-11 13:22:39 -05:00
for ( IntPtr pCurrent = ptr ; pCurrent ! = IntPtr . Zero ; )
2018-01-06 18:34:28 -05:00
{
2021-01-11 13:22:39 -05:00
T ret = pCurrent . ToStructure < T > ( ) ;
2018-01-06 18:34:28 -05:00
yield return ret ;
pCurrent = next ( ret ) ;
}
}
2020-11-04 15:04:42 -05:00
/// <summary>Marshals an unmanaged linked list of structures to an <see cref="IEnumerable{T}"/> of that structure.</summary>
/// <typeparam name="T">Type of native structure used by the unmanaged linked list.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="nextOffset">The expression to be used to fetch the offset from the current pointer to the next item in the list.</param>
/// <param name="allocatedBytes">
/// The number of allocated bytes behind <paramref name="ptr"/>. This value is used to determine when to stop enumerating.
/// </param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the linked list.</returns>
public static IEnumerable < T > LinkedListToIEnum < T > ( this IntPtr ptr , Func < T , long > nextOffset , SizeT allocatedBytes )
{
2021-01-11 13:22:39 -05:00
IntPtr pEnd = ptr . Offset ( allocatedBytes ) ;
for ( IntPtr pCurrent = ptr ; pCurrent . ToInt64 ( ) < pEnd . ToInt64 ( ) ; )
2020-11-04 15:04:42 -05:00
{
2021-01-11 13:22:39 -05:00
T ret = pCurrent . ToStructure < T > ( ) ;
2020-11-04 15:04:42 -05:00
yield return ret ;
2021-03-02 09:55:51 -05:00
var offset = nextOffset ( ret ) ;
if ( offset = = 0 ) break ;
pCurrent = pCurrent . Offset ( offset ) ;
2020-11-04 15:04:42 -05:00
}
}
2019-08-17 23:13:57 -04:00
/// <summary>
/// Marshals data from a managed list of objects to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
/// </summary>
/// <param name="values">The enumerated list of objects to marshal.</param>
/// <param name="memAlloc">
/// The function that allocates the memory for the block of objects (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <param name="referencePointers">
/// if set to <see langword="true"/> the pointer will be processed by storing a reference to the value; if <see langword="false"/>,
/// the pointer value will be directly inserted into the array of pointers.
/// </param>
/// <param name="charSet">The character set to use for strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the allocated objects.</param>
2021-01-11 13:22:39 -05:00
/// <returns>
/// Pointer to the allocated native (unmanaged) array of objects stored using the character set defined by <paramref name="charSet"/>.
/// </returns>
2019-08-17 23:13:57 -04:00
public static IntPtr MarshalObjectsToPtr ( this IEnumerable < object > values , Func < int , IntPtr > memAlloc , out int bytesAllocated , bool referencePointers = false , CharSet charSet = CharSet . Auto , int prefixBytes = 0 )
{
// Bail early if empty
if ( values is null | | ! values . Any ( ) )
{
bytesAllocated = prefixBytes + IntPtr . Size ;
2021-01-11 13:22:39 -05:00
IntPtr ret = memAlloc ( bytesAllocated ) ;
2019-08-17 23:13:57 -04:00
ret . FillMemory ( 0 , bytesAllocated ) ;
return ret ;
}
// Write to memory stream
using ( var ms = new NativeMemoryStream ( 1024 , 1024 ) { CharSet = charSet } )
{
ms . SetLength ( ms . Position = prefixBytes ) ;
foreach ( var o in values )
{
if ( referencePointers )
ms . WriteReferenceObject ( o ) ;
else
ms . WriteObject ( o ) ;
}
if ( referencePointers ) ms . WriteReference ( null ) ;
ms . Flush ( ) ;
// Copy to newly allocated memory using memAlloc
bytesAllocated = ( int ) ms . Length ;
2021-01-11 13:22:39 -05:00
IntPtr ret = memAlloc ( bytesAllocated ) ;
2019-08-17 23:13:57 -04:00
ms . Pointer . CopyTo ( ret , bytesAllocated ) ;
return ret ;
}
}
2017-11-27 12:18:01 -05:00
/// <summary>Marshals data from a managed list of specified type to a pre-allocated unmanaged block of memory.</summary>
/// <typeparam name="T">
2018-11-19 23:18:50 -05:00
/// A type of the enumerated managed object that holds the data to be marshaled. The object must be a structure or an instance of a
/// formatted class.
2017-11-27 12:18:01 -05:00
/// </typeparam>
/// <param name="items">The enumerated list of items to marshal.</param>
/// <param name="ptr">
2021-01-11 13:22:39 -05:00
/// A pointer to a pre-allocated block of memory. The allocated memory must be sufficient to hold the size of <typeparamref
/// name="T"/> times the number of items in the enumeration plus the number of bytes specified by <paramref name="prefixBytes"/>.
2017-11-27 12:18:01 -05:00
/// </param>
/// <param name="prefixBytes">The number of bytes to skip before writing the first element of <paramref name="items"/>.</param>
2019-08-17 23:13:57 -04:00
[Obsolete("Please use the Vanara.Extensions.InteropExtensions.Write method instead. This will be removed from the library shortly as it performs no allocation.", true)]
public static void MarshalToPtr < T > ( this IEnumerable < T > items , IntPtr ptr , int prefixBytes = 0 ) = > Write ( ptr , items , prefixBytes ) ;
/// <summary>Marshals data from a managed object to an unmanaged block of memory that is allocated using <paramref name="memAlloc"/>.</summary>
/// <typeparam name="T">The type of the managed object.</typeparam>
/// <param name="value">
/// A managed object that holds the data to be marshaled. The object must be a structure or an instance of a formatted class.
/// </param>
/// <param name="memAlloc">
/// The function that allocates the memory for the structure (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
2020-07-19 18:45:54 -04:00
/// <param name="memLock">
/// The function used to lock memory before assignment. If <see langword="null"/>, the result from <paramref name="memAlloc"/> will
/// be used.
/// </param>
/// <param name="memUnlock">The optional function to unlock memory after assignment.</param>
2019-08-17 23:13:57 -04:00
/// <returns>A pointer to the memory allocated by <paramref name="memAlloc"/>.</returns>
2022-01-16 19:21:11 -05:00
public static IntPtr MarshalToPtr < T > ( this T value , Func < int , IntPtr > memAlloc , out int bytesAllocated , int prefixBytes = 0 , Func < IntPtr , IntPtr > memLock = null , Func < IntPtr , bool > memUnlock = null )
2017-11-27 12:18:01 -05:00
{
2020-07-19 18:45:54 -04:00
memLock ? ? = Passthrough ;
2021-01-11 13:22:39 -05:00
if ( VanaraMarshaler . CanMarshal ( typeof ( T ) , out IVanaraMarshaler marshaler ) )
2019-11-19 14:52:44 -05:00
{
2021-01-11 13:22:39 -05:00
using SafeAllocatedMemoryHandle mem = marshaler . MarshalManagedToNative ( value ) ;
IntPtr ret = memAlloc ( bytesAllocated = mem . Size + prefixBytes ) ;
2020-07-19 18:45:54 -04:00
mem . DangerousGetHandle ( ) . CopyTo ( memLock ( ret ) . Offset ( prefixBytes ) , mem . Size ) ;
memUnlock ? . Invoke ( ret ) ;
2019-11-19 14:52:44 -05:00
return ret ;
}
else
{
var newVal = TrueValue ( value , out bytesAllocated ) ;
bytesAllocated + = prefixBytes ;
2021-01-11 13:22:39 -05:00
IntPtr ret = memAlloc ( bytesAllocated ) ;
2020-07-19 18:45:54 -04:00
Write ( memLock ( ret ) , newVal , prefixBytes , bytesAllocated ) ;
memUnlock ? . Invoke ( ret ) ;
2019-11-19 14:52:44 -05:00
return ret ;
}
2017-11-27 12:18:01 -05:00
}
2018-11-19 23:18:50 -05:00
/// <summary>
/// Marshals data from a managed list of specified type to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="T">
2018-11-19 23:18:50 -05:00
/// A type of the enumerated managed object that holds the data to be marshaled. The object must be a structure or an instance of a
/// formatted class.
2017-11-27 12:18:01 -05:00
/// </typeparam>
/// <param name="items">The enumerated list of items to marshal.</param>
2018-11-19 23:18:50 -05:00
/// <param name="memAlloc">
/// The function that allocates the memory for the block of items (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
2017-11-27 12:18:01 -05:00
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
2020-07-19 18:45:54 -04:00
/// <param name="memLock">
/// The function used to lock memory before assignment. If <see langword="null"/>, the result from <paramref name="memAlloc"/> will
/// be used.
/// </param>
/// <param name="memUnlock">The optional function to unlock memory after assignment.</param>
2017-11-27 12:18:01 -05:00
/// <returns>Pointer to the allocated native (unmanaged) array of items stored.</returns>
/// <exception cref="ArgumentException">Structure layout is not sequential or explicit.</exception>
2022-01-16 19:21:11 -05:00
public static IntPtr MarshalToPtr < T > ( this IEnumerable < T > items , Func < int , IntPtr > memAlloc , out int bytesAllocated , int prefixBytes = 0 , Func < IntPtr , IntPtr > memLock = null , Func < IntPtr , bool > memUnlock = null )
2017-11-27 12:18:01 -05:00
{
2019-12-06 19:07:21 -05:00
if ( ! typeof ( T ) . IsMarshalable ( ) ) throw new ArgumentException ( @"Structure layout is not sequential or explicit." ) ;
2017-11-27 12:18:01 -05:00
2020-07-19 18:45:54 -04:00
memLock ? ? = Passthrough ;
2017-11-27 12:18:01 -05:00
bytesAllocated = prefixBytes ;
2019-03-27 00:03:39 -04:00
var count = items ? . Count ( ) ? ? 0 ;
2017-11-27 12:18:01 -05:00
if ( count = = 0 ) return memAlloc ( bytesAllocated ) ;
var sz = Marshal . SizeOf ( typeof ( T ) ) ;
bytesAllocated + = sz * count ;
2021-01-11 13:22:39 -05:00
IntPtr result = memAlloc ( bytesAllocated ) ;
2020-07-19 18:45:54 -04:00
memLock ( result ) . Write ( items , prefixBytes , bytesAllocated ) ;
memUnlock ? . Invoke ( result ) ;
2017-11-27 12:18:01 -05:00
return result ;
}
2019-08-17 23:13:57 -04:00
/// <summary>
/// Marshals data from an array of a specified type to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
/// </summary>
/// <typeparam name="T">
/// A type of the array element that holds the data to be marshaled. The object must be a structure or an instance of a formatted class.
/// </typeparam>
/// <param name="items">The array of items to marshal.</param>
/// <param name="memAlloc">
/// The function that allocates the memory for the block of items (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
2020-07-19 18:45:54 -04:00
/// <param name="memLock">
/// The function used to lock memory before assignment. If <see langword="null"/>, the result from <paramref name="memAlloc"/> will
/// be used.
/// </param>
/// <param name="memUnlock">The optional function to unlock memory after assignment.</param>
2019-08-17 23:13:57 -04:00
/// <returns>Pointer to the allocated native (unmanaged) array of items stored.</returns>
/// <exception cref="ArgumentException">Structure layout is not sequential or explicit.</exception>
2022-01-16 19:21:11 -05:00
public static IntPtr MarshalToPtr < T > ( this T [ ] items , Func < int , IntPtr > memAlloc , out int bytesAllocated , int prefixBytes = 0 , Func < IntPtr , IntPtr > memLock = null , Func < IntPtr , bool > memUnlock = null ) = >
2020-07-19 18:45:54 -04:00
MarshalToPtr ( items . Cast < T > ( ) , memAlloc , out bytesAllocated , prefixBytes , memLock , memUnlock ) ;
2019-08-17 23:13:57 -04:00
2018-11-19 23:18:50 -05:00
/// <summary>
/// Marshals data from a managed list of strings to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="values">The enumerated list of strings to marshal.</param>
/// <param name="packing">The packing type for the strings.</param>
/// <param name="memAlloc">
/// The function that allocates the memory for the block of strings (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <param name="charSet">The character set to use for the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
2020-07-19 18:45:54 -04:00
/// <param name="memLock">
/// The function used to lock memory before assignment. If <see langword="null"/>, the result from <paramref name="memAlloc"/> will
/// be used.
/// </param>
/// <param name="memUnlock">The optional function to unlock memory after assignment.</param>
2017-11-27 12:18:01 -05:00
/// <returns>
2018-11-19 23:18:50 -05:00
/// Pointer to the allocated native (unmanaged) array of strings stored using the <paramref name="packing"/> model and the character
/// set defined by <paramref name="charSet"/>.
2017-11-27 12:18:01 -05:00
/// </returns>
2022-01-16 19:21:11 -05:00
public static IntPtr MarshalToPtr ( this IEnumerable < string > values , StringListPackMethod packing , Func < int , IntPtr > memAlloc , out int bytesAllocated , CharSet charSet = CharSet . Auto , int prefixBytes = 0 , Func < IntPtr , IntPtr > memLock = null , Func < IntPtr , bool > memUnlock = null )
2017-11-27 12:18:01 -05:00
{
2020-07-19 18:45:54 -04:00
memLock ? ? = Passthrough ;
2019-04-10 14:13:28 -04:00
// Bail early if empty
if ( values is null | | ! values . Any ( ) )
2017-11-27 12:18:01 -05:00
{
2019-04-10 14:13:28 -04:00
bytesAllocated = prefixBytes + ( packing = = StringListPackMethod . Concatenated ? StringHelper . GetCharSize ( charSet ) : IntPtr . Size ) ;
2021-01-11 13:22:39 -05:00
IntPtr nret = memAlloc ( bytesAllocated ) ;
memLock ( nret ) . FillMemory ( 0 , bytesAllocated ) ;
memUnlock ? . Invoke ( nret ) ;
return nret ;
2017-11-27 12:18:01 -05:00
}
2019-04-10 14:13:28 -04:00
// Write to memory stream
2021-01-11 13:22:39 -05:00
using var ms = StringsToStream ( values , packing , charSet , prefixBytes ) ;
2017-11-27 12:18:01 -05:00
2021-01-11 13:22:39 -05:00
// Copy to newly allocated memory using memAlloc
bytesAllocated = ( int ) ms . Length ;
IntPtr ret = memAlloc ( bytesAllocated ) ;
ms . Pointer . CopyTo ( memLock ( ret ) , bytesAllocated ) ;
memUnlock ? . Invoke ( ret ) ;
return ret ;
2017-11-27 12:18:01 -05:00
}
2019-03-07 11:22:04 -05:00
/// <summary>
2019-08-17 23:13:57 -04:00
/// Marshals data from a managed array of strings to an unmanaged block of memory allocated by the <paramref name="memAlloc"/> method.
2019-03-07 11:22:04 -05:00
/// </summary>
2019-08-17 23:13:57 -04:00
/// <param name="values">The array of strings to marshal.</param>
/// <param name="packing">The packing type for the strings.</param>
2019-03-07 11:22:04 -05:00
/// <param name="memAlloc">
2019-08-17 23:13:57 -04:00
/// The function that allocates the memory for the block of strings (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
2019-03-07 11:22:04 -05:00
/// </param>
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
2019-08-17 23:13:57 -04:00
/// <param name="charSet">The character set to use for the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the trailing strings.</param>
2020-07-19 18:45:54 -04:00
/// <param name="memLock">
/// The function used to lock memory before assignment. If <see langword="null"/>, the result from <paramref name="memAlloc"/> will
/// be used.
/// </param>
/// <param name="memUnlock">The optional function to unlock memory after assignment.</param>
2019-08-17 23:13:57 -04:00
/// <returns>
/// Pointer to the allocated native (unmanaged) array of strings stored using the <paramref name="packing"/> model and the character
/// set defined by <paramref name="charSet"/>.
/// </returns>
2022-01-16 19:21:11 -05:00
public static IntPtr MarshalToPtr ( this string [ ] values , StringListPackMethod packing , Func < int , IntPtr > memAlloc , out int bytesAllocated , CharSet charSet = CharSet . Auto , int prefixBytes = 0 , Func < IntPtr , IntPtr > memLock = null , Func < IntPtr , bool > memUnlock = null ) = >
2020-07-19 18:45:54 -04:00
MarshalToPtr ( ( IEnumerable < string > ) values , packing , memAlloc , out bytesAllocated , charSet , prefixBytes , memLock , memUnlock ) ;
2019-03-07 11:22:04 -05:00
2017-11-27 12:18:01 -05:00
/// <summary>Adds an offset to the value of a pointer.</summary>
/// <param name="pointer">The pointer to add the offset to.</param>
/// <param name="offset">The offset to add.</param>
/// <returns>A new pointer that reflects the addition of <paramref name="offset"/> to <paramref name="pointer"/>.</returns>
public static IntPtr Offset ( this IntPtr pointer , long offset ) = > new IntPtr ( pointer . ToInt64 ( ) + offset ) ;
2019-01-08 10:17:45 -05:00
/// <summary>Queries the object for a COM interface and returns it, if found, in <paramref name="ppv"/>.</summary>
/// <param name="iUnk">The object to query.</param>
/// <param name="iid">The interface identifier (IID) of the requested interface.</param>
/// <param name="ppv">When this method returns, contains a reference to the returned interface.</param>
/// <returns>An HRESULT that indicates the success or failure of the call.</returns>
2020-07-12 09:54:40 -04:00
public static int QueryInterface ( object iUnk , Guid iid , out object ppv )
2019-01-08 10:17:45 -05:00
{
2021-01-11 13:22:39 -05:00
var hr = Marshal . QueryInterface ( Marshal . GetIUnknownForObject ( iUnk ) , ref iid , out IntPtr ippv ) ;
2019-01-08 10:17:45 -05:00
ppv = hr = = 0 ? Marshal . GetObjectForIUnknown ( ippv ) : null ;
return hr ;
}
2019-11-19 14:52:44 -05:00
/// <summary>Returns the native memory size of a type, if possible.</summary>
/// <typeparam name="T">The type whose size is to be returned.</typeparam>
/// <returns>The size, in bytes, of the type that is specified by the <typeparamref name="T"/> type parameter.</returns>
/// <exception cref="ArgumentException">Unable to get size of type.</exception>
public static SizeT SizeOf < T > ( ) = > SizeOf ( typeof ( T ) ) ;
/// <summary>Returns the native memory size of a type, if possible.</summary>
/// <param name="type">The type whose size is to be returned.</param>
/// <returns>The size, in bytes, of the type that is specified by the <paramref name="type"/> parameter.</returns>
/// <exception cref="ArgumentException">Unable to get size of type. - type</exception>
public static SizeT SizeOf ( Type type )
{
2021-01-11 13:22:39 -05:00
if ( VanaraMarshaler . CanMarshal ( type , out IVanaraMarshaler marshaler ) )
2019-11-19 14:52:44 -05:00
return marshaler . GetNativeSize ( ) ;
return type . IsEnum ? Marshal . SizeOf ( Enum . GetUnderlyingType ( type ) ) : Marshal . SizeOf ( type ) ;
}
2021-01-20 18:53:12 -05:00
/// <summary>Returns the native memory size of a value, if possible.</summary>
/// <param name="value">The value whose native size is to be returned.</param>
/// <param name="charSet">The character set to use for the strings.</param>
/// <returns>The size, in bytes, of the value that is specified by the <paramref name="value"/> parameter.</returns>
/// <exception cref="ArgumentException">Unable to get the size of the value.</exception>
public static SizeT SizeOf ( object value , CharSet charSet = CharSet . Auto )
{
if ( value is null ) return 0 ;
if ( value is string s ) return StringHelper . GetByteCount ( s , true , charSet ) ;
var valType = value . GetType ( ) ;
var elemType = valType . FindElementType ( ) ;
if ( elemType is null )
return SizeOf ( valType ) ;
if ( elemType = = typeof ( string ) )
return ( ( System . Collections . IEnumerable ) value ) . Cast < string > ( ) . Sum ( s = > StringHelper . GetByteCount ( s , true , charSet ) ) + StringHelper . GetCharSize ( ) ;
if ( valType . IsArray )
return ( ( Array ) value ) . Length * SizeOf ( elemType ) ;
if ( value is System . Collections . ICollection ic )
return ic . Count * SizeOf ( elemType ) ;
if ( value is System . Collections . IEnumerable ie )
return ie . Cast < object > ( ) . Count ( ) * SizeOf ( elemType ) ;
throw new ArgumentException ( "Unable to get the size of the value." ) ;
}
2017-11-27 12:18:01 -05:00
/// <summary>Marshals data from a managed object to an unmanaged block of memory that is allocated using <paramref name="memAlloc"/>.</summary>
/// <typeparam name="T">The type of the managed object.</typeparam>
2018-11-19 23:18:50 -05:00
/// <param name="value">
/// A managed object that holds the data to be marshaled. The object must be a structure or an instance of a formatted class.
/// </param>
/// <param name="memAlloc">
/// The function that allocates the memory for the structure (typically <see cref="Marshal.AllocCoTaskMem(int)"/> or <see cref="Marshal.AllocHGlobal(int)"/>.
/// </param>
2017-11-27 12:18:01 -05:00
/// <param name="bytesAllocated">The bytes allocated by the <paramref name="memAlloc"/> method.</param>
/// <returns>A pointer to the memory allocated by <paramref name="memAlloc"/>.</returns>
2019-08-17 23:13:57 -04:00
[Obsolete("This function has been renamed MarshalToPtr for consistency. Please migrate your usage as this method will be removed in subsequent releases.")]
public static IntPtr StructureToPtr < T > ( this T value , Func < int , IntPtr > memAlloc , out int bytesAllocated ) = > MarshalToPtr ( value , memAlloc , out bytesAllocated ) ;
2017-11-27 12:18:01 -05:00
2017-12-12 20:41:40 -05:00
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into a CLI array.</summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
2019-08-17 23:13:57 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2017-11-27 12:18:01 -05:00
/// <returns>An array of type <typeparamref name="T"/> containing the elements of the native array.</returns>
2019-08-17 23:13:57 -04:00
public static T [ ] ToArray < T > ( this IntPtr ptr , int count , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
ToArray ( ptr , typeof ( T ) , count , prefixBytes , allocatedBytes ) . ToTypedArray < T > ( ) ;
2017-11-27 12:18:01 -05:00
2019-03-15 18:15:48 -04:00
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into a CLI array.</summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="type">Type of native structure used by the C-style array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
2019-08-17 23:13:57 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2019-03-15 18:15:48 -04:00
/// <returns>An array of type <paramref name="type"/> containing the elements of the native array.</returns>
2019-08-17 23:13:57 -04:00
public static Array ToArray ( this IntPtr ptr , Type type , int count , int prefixBytes = 0 , SizeT allocatedBytes = default )
2019-03-15 18:15:48 -04:00
{
if ( type is null ) throw new ArgumentNullException ( nameof ( type ) ) ;
if ( ptr = = IntPtr . Zero ) return null ;
2019-08-17 23:13:57 -04:00
var ret = Array . CreateInstance ( type , count ) ; // new object[count];
2021-01-11 13:22:39 -05:00
SizeT stSize = SizeOf ( type ) ;
2019-08-17 23:13:57 -04:00
if ( allocatedBytes > 0 & & stSize * count + prefixBytes > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
2019-11-25 21:32:51 -05:00
if ( allocatedBytes = = default ) allocatedBytes = uint . MaxValue ;
2019-03-15 18:15:48 -04:00
for ( var i = 0 ; i < count ; i + + )
2019-11-19 14:52:44 -05:00
{
var offset = prefixBytes + i * stSize ;
ret . SetValue ( ptr . Offset ( offset ) . Convert ( allocatedBytes - ( uint ) offset , type ) , i ) ;
}
2019-03-15 18:15:48 -04:00
return ret ;
}
2017-12-12 20:41:40 -05:00
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="IEnumerable{T}"/>.</summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
2019-08-17 23:13:57 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2017-11-27 12:18:01 -05:00
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
2019-08-17 23:13:57 -04:00
public static IEnumerable < T > ToIEnum < T > ( this IntPtr ptr , int count , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
2020-05-15 18:37:15 -04:00
new NativeMemoryEnumerator < T > ( ptr , count , prefixBytes , allocatedBytes ) ;
2017-11-27 12:18:01 -05:00
2019-03-15 18:15:48 -04:00
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="IEnumerable{T}"/>.</summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="type">Type of native structure used by the C-style array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
2019-08-17 23:13:57 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2019-03-15 18:15:48 -04:00
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
2020-05-15 18:37:15 -04:00
public static System . Collections . IEnumerable ToIEnum ( this IntPtr ptr , Type type , int count , int prefixBytes = 0 , SizeT allocatedBytes = default ) = >
new UntypedNativeMemoryEnumerator ( ptr , type , count , prefixBytes , allocatedBytes ) ;
2019-03-15 18:15:48 -04:00
2017-11-27 12:18:01 -05:00
/// <summary>Converts a <see cref="SecureString"/> to a string.</summary>
/// <param name="s">The <see cref="SecureString"/> value.</param>
/// <returns>The extracted string.</returns>
public static string ToInsecureString ( this SecureString s )
{
if ( s = = null ) return null ;
2021-01-11 13:22:39 -05:00
IntPtr p = IntPtr . Zero ;
2017-11-27 12:18:01 -05:00
try
{
p = Marshal . SecureStringToCoTaskMemUnicode ( s ) ;
return Marshal . PtrToStringUni ( p ) ;
}
finally
{
if ( p ! = IntPtr . Zero )
Marshal . ZeroFreeCoTaskMemUnicode ( p ) ;
}
}
2018-11-19 23:18:50 -05:00
/// <summary>Converts a <see cref="UIntPtr"/> to a <see cref="IntPtr"/>.</summary>
/// <param name="p">The <see cref="UIntPtr"/>.</param>
/// <returns>An equivalent <see cref="IntPtr"/>.</returns>
public static IntPtr ToIntPtr ( this UIntPtr p )
{
unsafe { return new IntPtr ( p . ToPointer ( ) ) ; }
}
/// <summary>Converts an <see cref="IntPtr"/> to a structure. If pointer has no value, <c>null</c> is returned.</summary>
/// <typeparam name="T">Type of the structure.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> that points to allocated memory holding a structure or <see cref="IntPtr.Zero"/>.</param>
/// <returns>The converted structure or <c>null</c>.</returns>
public static T ? ToNullableStructure < T > ( this IntPtr ptr ) where T : struct = > ptr ! = IntPtr . Zero ? ptr . ToStructure < T > ( ) : ( T ? ) null ;
2017-11-27 12:18:01 -05:00
/// <summary>Converts a pointer to an unmanaged Unicode string to a <see cref="SecureString"/>.</summary>
/// <param name="p">A pointer to an unmanaged Unicode string.</param>
/// <returns>A <see cref="SecureString"/> with the contents of the in memory string.</returns>
public static SecureString ToSecureString ( this IntPtr p )
{
if ( p = = IntPtr . Zero ) return null ;
var s = new SecureString ( ) ;
var i = 0 ;
while ( true )
{
var c = ( char ) Marshal . ReadInt16 ( p , ( ( i + + ) * sizeof ( short ) ) ) ;
if ( c = = ' \ u0000 ' )
break ;
s . AppendChar ( c ) ;
}
s . MakeReadOnly ( ) ;
return s ;
}
/// <summary>Converts a pointer to an unmanaged Unicode string of a specified length to a <see cref="SecureString"/>.</summary>
/// <param name="p">A pointer to an unmanaged Unicode string.</param>
/// <param name="length">The number of Unicode characters in the unmanaged string, excluding any terminating null values.</param>
/// <returns>A <see cref="SecureString"/> with the contents of the in memory string.</returns>
public static SecureString ToSecureString ( this IntPtr p , int length )
{
if ( p = = IntPtr . Zero ) return null ;
var s = new SecureString ( ) ;
for ( var i = 0 ; i < length ; i + + )
s . AppendChar ( ( char ) Marshal . ReadInt16 ( p , i * sizeof ( short ) ) ) ;
s . MakeReadOnly ( ) ;
return s ;
}
/// <summary>Converts a string to a <see cref="SecureString"/>.</summary>
/// <param name="s">A string.</param>
/// <returns>A <see cref="SecureString"/> with the contents of the string.</returns>
public static SecureString ToSecureString ( this string s )
{
if ( s = = null ) return null ;
var ss = new SecureString ( ) ;
foreach ( var c in s )
ss . AppendChar ( c ) ;
ss . MakeReadOnly ( ) ;
return ss ;
}
2018-11-19 23:18:50 -05:00
/// <summary>
2021-01-11 13:22:39 -05:00
/// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length
/// <paramref name="count"/>.
2018-11-19 23:18:50 -05:00
/// </summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The count of expected strings.</param>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
2019-03-25 20:34:03 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2018-11-19 23:18:50 -05:00
/// <returns>Enumeration of strings.</returns>
2019-08-17 23:13:57 -04:00
public static IEnumerable < string > ToStringEnum ( this IntPtr ptr , int count , CharSet charSet = CharSet . Auto , int prefixBytes = 0 , SizeT allocatedBytes = default )
2018-11-19 23:18:50 -05:00
{
if ( ptr = = IntPtr . Zero | | count = = 0 ) yield break ;
2019-08-17 23:13:57 -04:00
if ( allocatedBytes > 0 & & count * IntPtr . Size + prefixBytes > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
2018-11-19 23:18:50 -05:00
for ( var i = 0 ; i < count ; i + + )
{
2021-01-11 13:22:39 -05:00
IntPtr sptr = Marshal . ReadIntPtr ( ptr . Offset ( prefixBytes + i * IntPtr . Size ) ) ;
2018-11-19 23:18:50 -05:00
yield return StringHelper . GetString ( sptr , charSet ) ;
}
}
/// <summary>
2021-01-11 13:22:39 -05:00
/// Gets an enumerated list of strings from a block of unmanaged memory where each string is separated by a single '\0' character
/// and is terminated by two '\0' characters.
2018-11-19 23:18:50 -05:00
/// </summary>
/// <param name="lptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
2019-03-25 20:34:03 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="lptr"/>.</param>
2018-11-19 23:18:50 -05:00
/// <returns>An enumerated list of strings.</returns>
2019-08-17 23:13:57 -04:00
public static IEnumerable < string > ToStringEnum ( this IntPtr lptr , CharSet charSet = CharSet . Auto , int prefixBytes = 0 , SizeT allocatedBytes = default )
2018-11-19 23:18:50 -05:00
{
if ( lptr = = IntPtr . Zero ) yield break ;
var charLength = StringHelper . GetCharSize ( charSet ) ;
2019-03-25 20:34:03 -04:00
var i = prefixBytes ;
2022-01-16 19:21:11 -05:00
if ( allocatedBytes = = 0 )
allocatedBytes = SizeT . MaxValue ;
else if ( allocatedBytes - prefixBytes - charLength < 0 )
throw new InsufficientMemoryException ( ) ;
2021-05-02 23:51:20 -04:00
// handle condition where there is just the null terminator
2022-01-16 19:21:11 -05:00
else if ( allocatedBytes - prefixBytes - charLength = = 0 & & GetCh ( lptr . Offset ( prefixBytes ) ) = = 0 )
yield break ;
2021-01-11 13:22:39 -05:00
for ( IntPtr ptr = lptr . Offset ( i ) ; i + charLength < = allocatedBytes & & GetCh ( ptr ) ! = 0 ; i + = charLength , ptr = lptr . Offset ( i ) )
2018-11-19 23:18:50 -05:00
{
2021-01-11 13:22:39 -05:00
for ( IntPtr cptr = ptr ; i + charLength < = allocatedBytes & & GetCh ( cptr ) ! = 0 ; cptr = cptr . Offset ( charLength ) , i + = charLength ) { }
2019-08-17 23:13:57 -04:00
if ( i + charLength > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
2019-03-25 20:34:03 -04:00
yield return StringHelper . GetString ( ptr , charSet ) ;
//ptr = ptr.Offset(((s?.Length ?? 0) + 1) * charLength);
2018-11-19 23:18:50 -05:00
}
2019-03-25 20:34:03 -04:00
if ( i + charLength > allocatedBytes ) throw new InsufficientMemoryException ( ) ;
2019-08-17 23:13:57 -04:00
2021-01-11 13:22:39 -05:00
int GetCh ( IntPtr p ) = > charLength = = 1 ? Marshal . ReadByte ( p ) : Marshal . ReadInt16 ( p ) ;
2018-11-19 23:18:50 -05:00
}
/// <summary>
/// Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter.
/// </summary>
/// <typeparam name="T">The type of the object to which the data is to be copied. This must be a structure.</typeparam>
/// <param name="ptr">A pointer to an unmanaged block of memory.</param>
2019-03-27 00:03:39 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
2019-08-17 23:13:57 -04:00
/// <param name="offset">The number of bytes to skip before reading the element.</param>
/// <returns>A managed object that contains the data that the <paramref name="ptr"/> parameter points to.</returns>
2019-03-27 00:03:39 -04:00
/// <exception cref="InsufficientMemoryException"></exception>
2018-11-19 23:18:50 -05:00
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
2019-08-17 23:13:57 -04:00
public static T ToStructure < T > ( this IntPtr ptr , SizeT allocatedBytes = default , int offset = 0 )
2019-03-27 00:03:39 -04:00
{
2019-11-25 21:32:51 -05:00
if ( allocatedBytes = = default ) allocatedBytes = uint . MaxValue ;
2019-11-19 14:52:44 -05:00
return ptr . Offset ( offset ) . Convert < T > ( allocatedBytes - ( uint ) offset ) ;
2019-03-27 00:03:39 -04:00
}
2018-11-19 23:18:50 -05:00
/// <summary>Marshals data from an unmanaged block of memory to a managed object.</summary>
/// <typeparam name="T">The type of the object to which the data is to be copied. This must be a formatted class.</typeparam>
/// <param name="ptr">A pointer to an unmanaged block of memory.</param>
/// <param name="instance">The object to which the data is to be copied. This must be an instance of a formatted class.</param>
2019-08-17 23:13:57 -04:00
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <param name="offset">The number of bytes to skip before reading the element.</param>
2018-11-19 23:18:50 -05:00
/// <returns>A managed object that contains the data that the <paramref name="ptr"/> parameter points to.</returns>
2019-11-19 14:52:44 -05:00
public static void ToStructure < T > ( this IntPtr ptr , T instance , SizeT allocatedBytes = default , int offset = 0 ) where T : class
2018-11-19 23:18:50 -05:00
{
2019-08-17 23:13:57 -04:00
if ( ptr = = IntPtr . Zero ) throw new NullReferenceException ( ) ;
2021-01-11 13:22:39 -05:00
Type t = TrueType ( typeof ( T ) , out var stSize ) ;
2019-08-17 23:13:57 -04:00
if ( allocatedBytes > 0 & & allocatedBytes < stSize + offset )
throw new InsufficientMemoryException ( ) ;
if ( t = = typeof ( T ) )
2019-08-21 12:18:07 -04:00
Marshal . PtrToStructure ( ptr , instance ) ;
2019-08-17 23:13:57 -04:00
else
2021-01-11 13:22:39 -05:00
{
using var pin = new PinnedObject ( instance ) ;
( ( IntPtr ) pin ) . Write ( Marshal . PtrToStructure ( ptr . Offset ( offset ) , t ) ) ;
}
2018-11-19 23:18:50 -05:00
}
2019-08-17 23:13:57 -04:00
/// <summary>Converts a single-dimensional <see cref="Array"/> to an array of <typeparamref name="T"/>.</summary>
/// <typeparam name="T">
/// The type of the output array. All elements in the array supplied as <paramref name="input"/> must be of this type.
/// </typeparam>
/// <param name="input">The input array.</param>
/// <returns>An array of <typeparamref name="T"/> elements.</returns>
public static T [ ] ToTypedArray < T > ( this Array input ) = > input ? . Cast < T > ( ) . ToArray ( ) ;
2018-11-19 23:18:50 -05:00
/// <summary>Converts a <see cref="IntPtr"/> to a <see cref="UIntPtr"/>.</summary>
/// <param name="p">The <see cref="IntPtr"/>.</param>
/// <returns>An equivalent <see cref="UIntPtr"/>.</returns>
public static UIntPtr ToUIntPtr ( this IntPtr p )
{
unsafe { return new UIntPtr ( p . ToPointer ( ) ) ; }
}
2019-08-17 23:13:57 -04:00
2020-11-11 21:57:53 -05:00
/// <summary>Converts an unsafe structure pointer into a managed array.</summary>
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The pointer to the first structure in the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An array of type <typeparamref name="T"/> containing the elements of the native array.</returns>
public static unsafe T [ ] UnsafePtrToArray < T > ( T * ptr , int count , SizeT allocatedBytes = default ) where T : unmanaged
{
2021-01-11 13:22:39 -05:00
SizeT stSize = SizeOf < T > ( ) ;
2020-11-11 21:57:53 -05:00
if ( allocatedBytes > 0 & & stSize * count > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
if ( allocatedBytes = = default ) allocatedBytes = uint . MaxValue ;
var ret = new T [ count ] ;
for ( var i = 0 ; i < count ; i + + )
ret [ i ] = ptr [ i ] ;
return ret ;
}
2019-08-21 12:18:07 -04:00
/// <summary>Marshals data from a managed list of specified type to a pre-allocated unmanaged block of memory.</summary>
/// <typeparam name="T">
/// A type of the enumerated managed object that holds the data to be marshaled. The object must be a structure or an instance of a
/// formatted class.
/// </typeparam>
/// <param name="ptr">
2021-01-11 13:22:39 -05:00
/// A pointer to a pre-allocated block of memory. The allocated memory must be sufficient to hold the size of <typeparamref
/// name="T"/> times the number of items in the enumeration plus the number of bytes specified by <paramref name="offset"/>.
2019-08-21 12:18:07 -04:00
/// </param>
2019-08-17 23:13:57 -04:00
/// <param name="items">The enumerated list of items to marshal.</param>
2019-08-21 12:18:07 -04:00
/// <param name="offset">The number of bytes to skip before writing the first element of <paramref name="items"/>.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>The number of bytes written. The offset is not included.</returns>
2019-08-17 23:13:57 -04:00
/// <exception cref="ArgumentException">Structure layout is not sequential or explicit.</exception>
/// <exception cref="InsufficientMemoryException"></exception>
2019-08-21 12:18:07 -04:00
public static int Write < T > ( this IntPtr ptr , IEnumerable < T > items , int offset = 0 , SizeT allocatedBytes = default )
2019-08-17 23:13:57 -04:00
{
var count = items ? . Count ( ) ? ? 0 ;
2019-08-21 12:18:07 -04:00
if ( count = = 0 ) return 0 ;
2019-08-17 23:13:57 -04:00
2021-01-11 13:22:39 -05:00
Type ttype = TrueType ( typeof ( T ) , out var stSize ) ;
2019-12-06 19:07:21 -05:00
if ( ! ttype . IsMarshalable ( ) )
2019-08-17 23:13:57 -04:00
throw new ArgumentException ( @"Structure layout is not sequential or explicit." ) ;
var bytesReq = stSize * count + offset ;
if ( allocatedBytes > 0 & & bytesReq > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
var i = 0 ;
2019-12-06 19:07:21 -05:00
foreach ( var item in items . Select ( v = > Convert . ChangeType ( v , ttype ) ) . Where ( v = > v ! = null ) )
WriteNoChecks ( ptr , item , offset + i + + * stSize , allocatedBytes ) ;
2019-08-21 12:18:07 -04:00
return bytesReq - offset ;
2019-08-17 23:13:57 -04:00
}
2021-01-11 13:22:39 -05:00
/// <summary>Marshals data from a managed list of strings to a pre-allocated unmanaged block of memory.</summary>
/// <param name="ptr">
/// A pointer to a pre-allocated block of memory. The allocated memory must be sufficient to hold the size of all the strings in the
/// enumeration plus pointers or '\0' characters required by <paramref name="packing"/>.
/// </param>
/// <param name="items">The enumerated list of items to marshal.</param>
/// <param name="packing">The packing type for the strings.</param>
/// <param name="charSet">The character set to use for the strings.</param>
/// <param name="offset">The number of bytes to skip before writing the first element of <paramref name="items"/>.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>The number of bytes written. The offset is not included.</returns>
/// <exception cref="System.InsufficientMemoryException"></exception>
/// <exception cref="System.ArgumentException">Concatenated string arrays cannot contain empty or null strings.</exception>
/// <exception cref="ArgumentException">Structure layout is not sequential or explicit.</exception>
/// <exception cref="InsufficientMemoryException"></exception>
public static int Write ( this IntPtr ptr , IEnumerable < string > items , StringListPackMethod packing , CharSet charSet = CharSet . Auto , int offset = 0 , SizeT allocatedBytes = default )
{
// Bail early if empty
if ( items is null | | ! items . Any ( ) )
{
var bytesReq = offset + ( packing = = StringListPackMethod . Concatenated ? StringHelper . GetCharSize ( charSet ) : IntPtr . Size ) ;
if ( allocatedBytes > 0 & & bytesReq > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
ptr . FillMemory ( 0 , bytesReq ) ;
return bytesReq - offset ;
}
// Write to memory stream
using NativeMemoryStream ms = StringsToStream ( items , packing , charSet , offset ) ;
// Copy stream bits to pointer
if ( allocatedBytes > 0 & & ( int ) ms . Length > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
2023-01-02 12:32:35 -05:00
ms . Pointer . CopyTo ( offset , ptr . Offset ( offset ) , ( int ) ms . Length - offset ) ;
2021-01-11 13:22:39 -05:00
return ( int ) ms . Length ;
}
2019-08-21 12:18:07 -04:00
/// <summary>Writes the specified value to pre-allocated memory.</summary>
2019-08-17 23:13:57 -04:00
/// <param name="ptr">The address of the memory where the value is to be written.</param>
/// <param name="value">The value to write.</param>
2019-08-21 12:18:07 -04:00
/// <param name="offset">The number of bytes to offset from <paramref name="ptr"/> before writing.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>The number of bytes written. The offset is not included.</returns>
2019-08-17 23:13:57 -04:00
/// <exception cref="InsufficientMemoryException"></exception>
public static int Write ( this IntPtr ptr , object value , int offset = 0 , SizeT allocatedBytes = default )
{
if ( value is null ) return 0 ;
2021-01-11 13:22:39 -05:00
//if (!value.GetType().IsMarshalable())
// throw new ArgumentException(@"Value cannot be serialized to memory.", nameof(value));
2019-08-17 23:13:57 -04:00
return WriteNoChecks ( ptr , value , offset , allocatedBytes ) ;
}
/// <summary>Writes the specified value to pre-allocated memory.</summary>
/// <typeparam name="T">The type of the value to write.</typeparam>
/// <param name="ptr">The address of the memory where the value is to be written.</param>
/// <param name="value">The value to write.</param>
/// <param name="offset">The number of bytes to offset from <paramref name="ptr"/> before writing.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>The number of bytes written. The offset is not included.</returns>
/// <exception cref="InsufficientMemoryException"></exception>
2019-10-17 11:13:45 -04:00
public static int Write < T > ( this IntPtr ptr , in T value , int offset = 0 , SizeT allocatedBytes = default ) where T : struct = >
2019-08-17 23:13:57 -04:00
WriteNoChecks ( ptr , value , offset , allocatedBytes ) ;
2019-11-19 14:52:44 -05:00
internal static T GetValueType < T > ( IntPtr ptr , Type trueType = null , int offset = 0 , SizeT allocatedBytes = default ) = >
2019-11-13 23:02:51 -05:00
( T ) GetValueType ( ptr , typeof ( T ) , trueType , offset , allocatedBytes ) ;
2019-08-17 23:13:57 -04:00
2019-11-19 14:52:44 -05:00
internal static object GetValueType ( IntPtr ptr , Type type , Type trueType = null , int offset = 0 , SizeT allocatedBytes = default )
2019-08-17 23:13:57 -04:00
{
2019-11-19 14:52:44 -05:00
if ( allocatedBytes = = 0 )
allocatedBytes = SizeT . MaxValue ;
trueType ? ? = type . IsEnum ? Enum . GetUnderlyingType ( type ) : type ;
2021-01-11 13:22:39 -05:00
var obj = VanaraMarshaler . CanMarshal ( trueType , out IVanaraMarshaler marshaler ) ?
2019-11-19 14:52:44 -05:00
marshaler . MarshalNativeToManaged ( ptr . Offset ( offset ) , allocatedBytes ) :
Marshal . SizeOf ( trueType ) < = allocatedBytes ? Marshal . PtrToStructure ( ptr . Offset ( offset ) , trueType ) : throw new InsufficientMemoryException ( ) ;
return type = = trueType ? obj : type . IsEnum ? Enum . ToObject ( type , obj ) : Convert . ChangeType ( obj , type ) ;
2019-08-17 23:13:57 -04:00
}
2021-01-11 13:22:39 -05:00
internal static Type TrueType ( Type type , out int size )
{
Type ttype = type . IsEnum ? Enum . GetUnderlyingType ( type ) : type = = typeof ( bool ) ? typeof ( uint ) : type ;
try { size = Marshal . SizeOf ( ttype ) ; } catch { size = 0 ; }
return ttype ;
}
2019-08-17 23:13:57 -04:00
2019-12-10 08:55:54 -05:00
internal static int WriteNoChecks ( IntPtr ptr , object value , int offset , SizeT allocatedBytes )
2019-08-17 23:13:57 -04:00
{
2019-12-09 09:22:20 -05:00
if ( value is IEnumerable < byte > b ) value = b . ToArray ( ) ;
if ( value is byte [ ] ba )
{
if ( allocatedBytes > 0 & & offset + ba . Length > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
Marshal . Copy ( ba , 0 , ptr , ba . Length ) ;
2019-12-10 08:55:54 -05:00
return ba . Length ;
2019-12-09 09:22:20 -05:00
}
2021-01-11 13:22:39 -05:00
Type valType = value . GetType ( ) ;
// Handle marshaled items
if ( VanaraMarshaler . CanMarshal ( valType , out IVanaraMarshaler marshaler ) )
2019-11-19 14:52:44 -05:00
{
2021-01-11 13:22:39 -05:00
using SafeAllocatedMemoryHandle mem = marshaler . MarshalManagedToNative ( value ) ;
2019-11-19 14:52:44 -05:00
if ( allocatedBytes > 0 & & offset + mem . Size > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
mem . DangerousGetHandle ( ) . CopyTo ( ptr . Offset ( offset ) , mem . Size ) ;
return mem . Size ;
}
2021-01-11 13:22:39 -05:00
// Handle strings (risk is wrong CharSet)
if ( value is string s )
{
StringHelper . Write ( s , ptr . Offset ( offset ) , out var wrtn , true , CharSet . Auto , allocatedBytes = = 0 ? long . MaxValue : allocatedBytes ) ;
return wrtn ;
}
// Handle simple types
if ( valType . IsBlittable ( ) & & ! valType . IsArray )
2019-11-19 14:52:44 -05:00
{
var newVal = TrueValue ( value , out var cbValue ) ;
if ( allocatedBytes > 0 & & offset + cbValue > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
Marshal . StructureToPtr ( newVal , ptr . Offset ( offset ) , false ) ;
return cbValue ;
}
2021-01-11 13:22:39 -05:00
// Handle string lists
if ( value is IEnumerable < string > ies )
{
return ptr . Write ( ies , StringListPackMethod . Concatenated , CharSet . Auto , offset , allocatedBytes ) ;
}
// Handle arrays
if ( valType . IsArray & & ( ( Array ) value ) . Rank = = 1 )
{
Type ttype = TrueType ( valType . GetElementType ( ) , out var stSize ) ;
if ( ! ttype . IsMarshalable ( ) )
throw new ArgumentException ( @"Structure layout is not sequential or explicit." ) ;
var arr = ( Array ) value ;
var count = arr . Length ;
if ( count = = 0 ) return 0 ;
var bytesReq = stSize * count + offset ;
if ( allocatedBytes > 0 & & bytesReq > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
for ( var i = 0 ; i < count ; i + + )
{
var o = arr . GetValue ( i ) ;
if ( o is null ) continue ;
var v = Convert . ChangeType ( o , ttype ) ;
WriteNoChecks ( ptr , o , offset + i * stSize , allocatedBytes ) ;
}
return bytesReq - offset ;
}
// Handle enumerations
if ( IsGenericEnumerable ( valType ) )
{
Type ttype = TrueType ( valType . GetGenericArguments ( ) [ 0 ] , out var stSize ) ;
if ( ! ttype . IsMarshalable ( ) )
throw new ArgumentException ( @"Structure layout is not sequential or explicit." ) ;
var items = new List < object > ( ( ( System . Collections . IEnumerable ) value ) . Cast < object > ( ) ) ;
var count = items . Count ;
if ( count = = 0 ) return 0 ;
var bytesReq = stSize * count + offset ;
if ( allocatedBytes > 0 & & bytesReq > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
var i = 0 ;
foreach ( var item in items . Select ( v = > Convert . ChangeType ( v , ttype ) ) . Where ( v = > v ! = null ) )
WriteNoChecks ( ptr , item , offset + i + + * stSize , allocatedBytes ) ;
return bytesReq - offset ;
}
// Handle binary serialization
if ( valType . IsSerializable )
2019-12-09 09:22:20 -05:00
{
using var str = new NativeMemoryStream ( ) ;
var bf = new BinaryFormatter ( ) ;
bf . Serialize ( str , value ) ;
str . Flush ( ) ;
if ( allocatedBytes > 0 & & offset + str . Length > allocatedBytes )
throw new InsufficientMemoryException ( ) ;
str . Pointer . CopyTo ( ptr . Offset ( offset ) , str . Length ) ;
2019-12-10 08:55:54 -05:00
return ( int ) str . Length ;
2019-12-09 09:22:20 -05:00
}
2021-01-11 13:22:39 -05:00
2019-12-10 08:55:54 -05:00
throw new ArgumentException ( "Unable to convert object to its binary format." ) ;
2021-01-11 13:22:39 -05:00
static bool IsGenericEnumerable ( Type t )
{
var genArgs = t . GetGenericArguments ( ) ;
if ( genArgs . Length = = 1 & & typeof ( IEnumerable < > ) . MakeGenericType ( genArgs ) . IsAssignableFrom ( t ) )
return true ;
else
return t . BaseType ! = null & & IsGenericEnumerable ( t . BaseType ) ;
}
}
private static IntPtr Passthrough ( IntPtr p ) = > p ;
private static NativeMemoryStream StringsToStream ( IEnumerable < string > items , StringListPackMethod packing , CharSet charSet , int offset )
{
var ms = new NativeMemoryStream ( 1024 , 1024 ) { CharSet = charSet } ;
ms . SetLength ( ms . Position = offset ) ;
if ( packing = = StringListPackMethod . Packed )
{
foreach ( var s in items )
ms . WriteReference ( s ) ;
ms . WriteReference ( null ) ;
}
else
{
foreach ( var s in items )
{
if ( string . IsNullOrEmpty ( s ) ) throw new ArgumentException ( "Concatenated string arrays cannot contain empty or null strings." ) ;
ms . Write ( s ) ;
}
ms . Write ( "" ) ;
}
ms . Flush ( ) ;
return ms ;
2019-08-17 23:13:57 -04:00
}
2021-01-11 13:22:39 -05:00
private static object TrueValue ( object value , out int size ) = > Convert . ChangeType ( value , TrueType ( value . GetType ( ) , out size ) ) ;
2017-11-27 12:18:01 -05:00
}
2021-01-11 13:22:39 -05:00
}