using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using Vanara.InteropServices;
namespace Vanara.Extensions
{
/// Extension methods for System.Runtime.InteropServices.
public static partial class InteropExtensions
{
///
/// Fills the memory with a particular byte value. This is a very dangerous function that can cause memory
/// access errors if the provided is bigger than allocated memory of if the is not a
/// valid memory pointer.
///
/// The allocated memory pointer.
/// The byte value with which to fill the memory.
/// The number of bytes to fill with the value.
public static void FillMemory(this IntPtr ptr, byte value, int length)
{
if (ptr == IntPtr.Zero || length <= 0) return;
// Write multiples of 8 bytes first
var lval = value == 0 ? 0L : BitConverter.ToInt64(new byte[] { value, value, value, value, value, value, value, value }, 0);
for (var ofs = 0; ofs < length / 8; ofs++)
Marshal.WriteInt64(ptr, ofs * 8, lval);
// Write remaining bytes
for (var ofs = length - (length % 8); ofs < length; ofs++)
Marshal.WriteByte(ptr, length, value);
}
///
/// Gets the length of a null terminated array of pointers. This is a very dangerous function and can result in
/// memory access errors if the does not point to a null-terminated array of pointers.
///
/// The pointing to the native array.
///
/// The number of non-null pointers in the array. If is equal to IntPtr.Zero, this result is 0.
///
public static int GetNulledPtrArrayLength(this IntPtr lptr)
{
if (lptr == IntPtr.Zero) return 0;
var c = 0;
while (Marshal.ReadIntPtr(lptr, IntPtr.Size * c++) != IntPtr.Zero) ;
return c - 1;
}
/// Determines whether this type is formatted or blittable.
/// The type to check.
/// true if the specified type is blittable; otherwise, false.
public static bool IsBlittable(this Type T)
{
// 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; }
}
/// Determines whether this type is nullable (derived from ).
/// The type to check.
/// true if the specified type is nullable; otherwise, false.
public static bool IsNullable(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
/// Marshals an unmanaged linked list of structures to an of that structure.
/// Type of native structure used by the unmanaged linked list.
/// The pointing to the native array.
/// The expression to be used to fetch the pointer to the next item in the list.
/// An exposing the elements of the linked list.
public static IEnumerable LinkedListToIEnum(this IntPtr ptr, Func next)
{
for (var pCurrent = ptr; pCurrent != IntPtr.Zero;)
{
var ret = pCurrent.ToStructure();
yield return ret;
pCurrent = next(ret);
}
yield break;
}
/// Marshals data from a managed list of specified type to a pre-allocated unmanaged block of memory.
///
/// 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.
///
/// The enumerated list of items to marshal.
///
/// A pointer to a pre-allocated block of memory. The allocated memory must be sufficient to hold the size of times the number of items in the enumeration plus the number of bytes specified by .
///
/// The number of bytes to skip before writing the first element of .
public static void MarshalToPtr(this IEnumerable items, IntPtr ptr, int prefixBytes = 0)
{
if (items is null) return;
var stSize = Marshal.SizeOf(typeof(T));
var i = 0;
foreach (var item in items)
Marshal.StructureToPtr(item, ptr.Offset(prefixBytes + i++ * stSize), false);
}
///
/// Marshals data from a managed list of specified type to an unmanaged block of memory allocated by the method.
///
///
/// 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.
///
/// The enumerated list of items to marshal.
///
/// The function that allocates the memory for the block of items (typically or .
///
/// The bytes allocated by the method.
/// Number of bytes preceding the trailing strings.
/// Pointer to the allocated native (unmanaged) array of items stored.
/// Structure layout is not sequential or explicit.
public static IntPtr MarshalToPtr(this IEnumerable items, Func memAlloc, out int bytesAllocated, int prefixBytes = 0)
{
if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
bytesAllocated = prefixBytes;
var count = (items as IList)?.Count ?? (items as T[])?.Length ?? items?.Count() ?? 0;
if (count == 0) return memAlloc(bytesAllocated);
var sz = Marshal.SizeOf(typeof(T));
bytesAllocated += sz * count;
var result = memAlloc(bytesAllocated);
var ptr = result.Offset(prefixBytes);
foreach (var value in items)
{
Marshal.StructureToPtr(value, ptr, false);
ptr = ptr.Offset(sz);
}
return result;
}
///
/// Marshals data from a managed list of strings to an unmanaged block of memory allocated by the method.
///
/// The enumerated list of strings to marshal.
/// The packing type for the strings.
///
/// The function that allocates the memory for the block of strings (typically or .
///
/// The bytes allocated by the method.
/// The character set to use for the strings.
/// Number of bytes preceding the trailing strings.
///
/// Pointer to the allocated native (unmanaged) array of strings stored using the model and the character
/// set defined by .
///
public static IntPtr MarshalToPtr(this IEnumerable values, StringListPackMethod packing, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0)
{
// Convert to list to avoid multiple iterations
var list = values as IList ?? (values != null ? new List(values) : null);
// Look at count and bail early if 0
var count = values?.Count() ?? 0;
var chSz = StringHelper.GetCharSize(charSet);
bytesAllocated = prefixBytes + (packing == StringListPackMethod.Concatenated ? chSz : IntPtr.Size);
if (count == 0)
{
var ret = memAlloc(bytesAllocated);
ret.FillMemory(0, bytesAllocated);
return ret;
}
// Check for empty and/or null strings
if (packing == StringListPackMethod.Concatenated && list.Any(s => string.IsNullOrEmpty(s)))
throw new ArgumentException("Concatenated string arrays cannot contain empty or null strings.");
// Get size of output
var sumStrLen = list.Sum(s => s == null ? 0 : s.Length + 1);
bytesAllocated += sumStrLen * chSz;
if (packing == StringListPackMethod.Packed) bytesAllocated += (IntPtr.Size * count);
using (var ms = new MarshalingStream(memAlloc(bytesAllocated), bytesAllocated) { Position = prefixBytes, CharSet = charSet })
{
if (packing == StringListPackMethod.Packed)
{
ms.Position += (count + 1) * IntPtr.Size;
for (var i = 0; i < list.Count; i++)
{
ms.Poke(list[i] is null ? IntPtr.Zero : ms.Pointer.Offset(ms.Position), prefixBytes + (i * IntPtr.Size));
ms.Write(list[i]);
}
ms.Poke(IntPtr.Zero, prefixBytes + (count * IntPtr.Size));
}
else
{
foreach (var s in list)
ms.Write(s);
ms.Write("");
}
return ms.Pointer;
}
}
///
/// Marshals data from a managed list of objects to an unmanaged block of memory allocated by the method.
///
/// The enumerated list of objects to marshal.
///
/// The function that allocates the memory for the block of objects (typically or .
///
/// The bytes allocated by the method.
///
/// if set to the pointer will be processed by storing a reference to the value; if ,
/// the pointer value will be directly inserted into the array of pointers.
///
/// The character set to use for strings.
/// Number of bytes preceding the allocated objects.
/// Pointer to the allocated native (unmanaged) array of objects stored using the character set defined by .
public static IntPtr MarshalObjectsToPtr(this IEnumerable