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 values, Func memAlloc, out int bytesAllocated, bool referencePointers = false, 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; bytesAllocated = prefixBytes + IntPtr.Size; if (count == 0) { var ret = memAlloc(bytesAllocated); ret.FillMemory(0, bytesAllocated); return ret; } // Get size of output (array size + objects sizes) var chSz = StringHelper.GetCharSize(charSet); bytesAllocated += IntPtr.Size * count; bytesAllocated += list.Sum(GetSize); // Create pointer array var ptrs = new IntPtr[count + 1]; using (var ms = new MarshalingStream(memAlloc(bytesAllocated), bytesAllocated) { Position = prefixBytes, CharSet = charSet }) { ms.Position += (count + 1) * IntPtr.Size; for (var i = 0; i < list.Count; i++) { if (!referencePointers && list[i] is IntPtr p) { ptrs[i] = p; } else { ptrs[i] = list[i] is null ? IntPtr.Zero : ms.Pointer.Offset(ms.Position); ms.Write(list[i]); } } ms.Position = 0; ms.Write(ptrs); return ms.Pointer; } int GetSize(object o) { if (o is null) return 0; if (o is string s) return (s.Length + 1) * chSz; if (o is IntPtr) return referencePointers ? IntPtr.Size : 0; return Marshal.SizeOf(o); } } /// 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); /// Queries the object for a COM interface and returns it, if found, in . /// The object to query. /// The interface identifier (IID) of the requested interface. /// When this method returns, contains a reference to the returned interface. /// An HRESULT that indicates the success or failure of the call. public static int QueryInterface(object iUnk, in Guid iid, out object ppv) { var tmp = iid; var hr = Marshal.QueryInterface(Marshal.GetIUnknownForObject(iUnk), ref tmp, out var ippv); ppv = hr == 0 ? Marshal.GetObjectForIUnknown(ippv) : null; return hr; } /// 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 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 to a . /// The . /// An equivalent . public static IntPtr ToIntPtr(this UIntPtr p) { unsafe { return new IntPtr(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; /// 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; } /// /// 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 || count == 0) 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); } } /// /// 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) => typeof(T) == typeof(IntPtr) ? (T)(object)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; } /// Converts a to a . /// The . /// An equivalent . public static UIntPtr ToUIntPtr(this IntPtr p) { unsafe { return new UIntPtr(p.ToPointer()); } } } }