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 { /// 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 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) { 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); Marshal.Copy(new byte[bytesAllocated], 0, ret, 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 (int i = 0; i < list.Count; i++) { ms.Poke(list[i] == 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; } } /// Adds an offset to the value of a pointer. /// The pointer to add the offset to. /// The offset to add. /// A new pointer that reflects the addition of to . public static IntPtr Offset(this IntPtr pointer, long offset) => new IntPtr(pointer.ToInt64() + offset); /// Marshals data from a managed object to an unmanaged block of memory that is allocated using . /// The type of the managed object. /// A managed object that holds the data to be marshaled. The object must be a structure or an instance of a formatted class. /// The function that allocates the memory for the structure (typically or . /// The bytes allocated by the method. /// A pointer to the memory allocated by . public static IntPtr StructureToPtr(this T value, Func memAlloc, out int bytesAllocated) { bytesAllocated = Marshal.SizeOf(value); var ret = memAlloc(bytesAllocated); Marshal.StructureToPtr(value, ret, false); return ret; } /// Converts an that points to a C-style array into a CLI array. /// Type of native structure used by the C-style array. /// The pointing to the native array. /// The number of items in the native array. /// Bytes to skip before reading the array. /// An array of type containing the elements of the native array. public static T[] ToArray(this IntPtr ptr, int count, int prefixBytes = 0) { if (ptr == IntPtr.Zero) return null; var ret = new T[count]; var stSize = Marshal.SizeOf(typeof(T)); for (var i = 0; i < count; i++) ret[i] = ToStructure(ptr.Offset(prefixBytes + i * stSize)); return ret; } /// Converts an that points to a C-style array into an . /// Type of native structure used by the C-style array. /// The pointing to the native array. /// The number of items in the native array. /// Bytes to skip before reading the array. /// An exposing the elements of the native array. public static IEnumerable ToIEnum(this IntPtr ptr, int count, int prefixBytes = 0) { if (count == 0 || ptr == IntPtr.Zero) yield break; var stSize = Marshal.SizeOf(typeof(T)); for (var i = 0; i < count; i++) yield return ToStructure(ptr.Offset(prefixBytes + i * stSize)); } /// Converts a to a . /// The . /// An equivalent . public static IntPtr ToIntPtr(this UIntPtr p) { unsafe { return new IntPtr(p.ToPointer()); } } /// Converts a to a . /// The . /// An equivalent . public static UIntPtr ToUIntPtr(this IntPtr p) { unsafe { return new UIntPtr(p.ToPointer()); } } /// Converts an to a structure. If pointer has no value, null is returned. /// Type of the structure. /// The that points to allocated memory holding a structure or . /// The converted structure or null. public static T? ToNullableStructure(this IntPtr ptr) where T : struct => ptr != IntPtr.Zero ? ptr.ToStructure() : (T?)null; /// Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter. /// The type of the object to which the data is to be copied. This must be a structure. /// A pointer to an unmanaged block of memory. /// A managed object that contains the data that the parameter points to. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public static T ToStructure(this IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T).IsEnum ? Enum.GetUnderlyingType(typeof(T)) : typeof(T)); /// Marshals data from an unmanaged block of memory to a managed object. /// The type of the object to which the data is to be copied. This must be a formatted class. /// A pointer to an unmanaged block of memory. /// The object to which the data is to be copied. This must be an instance of a formatted class. /// A managed object that contains the data that the parameter points to. public static T ToStructure(this IntPtr ptr, [In] T instance) { Marshal.PtrToStructure(ptr, instance); return instance; } /// /// 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; } /// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length . /// The pointing to the native array. /// The count of expected strings. /// The character set of the strings. /// Number of bytes preceding the array of string pointers. /// Enumeration of strings. public static IEnumerable ToStringEnum(this IntPtr ptr, int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) { if (ptr == IntPtr.Zero) yield break; var lPtrVal = ptr.ToInt64(); for (var i = 0; i < count; i++) { var iptr = new IntPtr(lPtrVal + prefixBytes + i * IntPtr.Size); var sptr = Marshal.ReadIntPtr(iptr); yield return StringHelper.GetString(sptr, charSet); } } /// /// 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. /// /// The pointing to the native array. /// The character set of the strings. /// Number of bytes preceding the array of string pointers. /// An enumerated list of strings. public static IEnumerable ToStringEnum(this IntPtr lptr, CharSet charSet = CharSet.Auto, int prefixBytes = 0) { if (lptr == IntPtr.Zero) yield break; var charLength = StringHelper.GetCharSize(charSet); int GetCh(IntPtr p) => charLength == 1 ? Marshal.ReadByte(p) : Marshal.ReadInt16(p); for (var ptr = lptr.Offset(prefixBytes); GetCh(ptr) != 0;) { var s = StringHelper.GetString(ptr, charSet); yield return s; ptr = ptr.Offset(((s?.Length ?? 0) + 1) * charLength); } } /// Converts a to a string. /// The value. /// The extracted string. public static string ToInsecureString(this SecureString s) { if (s == null) return null; var p = IntPtr.Zero; try { p = Marshal.SecureStringToCoTaskMemUnicode(s); return Marshal.PtrToStringUni(p); } finally { if (p != IntPtr.Zero) Marshal.ZeroFreeCoTaskMemUnicode(p); } } /// Converts a pointer to an unmanaged Unicode string to a . /// A pointer to an unmanaged Unicode string. /// A with the contents of the in memory string. 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; } /// Converts a pointer to an unmanaged Unicode string of a specified length to a . /// A pointer to an unmanaged Unicode string. /// The number of Unicode characters in the unmanaged string, excluding any terminating null values. /// A with the contents of the in memory string. 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; } /// Converts a string to a . /// A string. /// A with the contents of the string. 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; } } }