using System; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using Vanara.Extensions; namespace Vanara.InteropServices { /// Functions to safely convert a memory pointer to a type. public static class IntPtrConverter { /// Converts the specified pointer to . /// The destination type. /// The pointer to a block of memory. /// The size of the allocated memory block. /// The character set. /// A value of the type specified. public static T Convert(this IntPtr ptr, uint sz, CharSet charSet = CharSet.Auto) => (T)Convert(ptr, sz, typeof(T), charSet); /// Converts the specified pointer to type specified in . /// The pointer to a block of memory. /// The size of the allocated memory block. /// The destination type. /// The character set. /// A value of the type specified. /// Cannot convert a null pointer. - ptr or Cannot convert a pointer with no Size. - sz /// Thrown if type cannot be converted from memory. /// public static object Convert(this IntPtr ptr, uint sz, Type destType, CharSet charSet = CharSet.Auto) { if (ptr == IntPtr.Zero) { if (!destType.IsValueType) return null; throw new NullReferenceException(); } if (sz == 0) throw new ArgumentException("Cannot convert a pointer with no Size.", nameof(sz)); // Handle byte array and pointer as special cases if (destType.IsArray && destType.GetElementType() == typeof(byte)) return ptr.ToArray((int)sz); if (destType == typeof(IntPtr)) return Marshal.ReadIntPtr(ptr); var typeCode = Type.GetTypeCode(destType); switch (typeCode) { case TypeCode.Object: try { if (VanaraMarshaler.CanMarshal(destType, out var marshaler)) { return marshaler.MarshalNativeToManaged(ptr, sz); } if (destType.IsBlittable()) { return GetBlittable(destType); } if (destType.IsNullable()) { return ptr != IntPtr.Zero ? InteropExtensions.GetValueType(ptr, Nullable.GetUnderlyingType(destType)) : Activator.CreateInstance(destType, true); } if (destType.IsSerializable) { using var mem = new MemoryStream(ptr.ToArray((int)sz)); return new BinaryFormatter().Deserialize(mem); } } catch (ArgumentOutOfRangeException) { throw; } catch { } throw new NotSupportedException("Unsupported type parameter."); case TypeCode.Boolean: return System.Convert.ChangeType(GetBlittable(typeof(uint)), typeCode); case TypeCode.Char: return System.Convert.ChangeType(GetBlittable(typeof(ushort)), typeCode); case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: return GetBlittable(destType); case TypeCode.DateTime: return DateTime.FromBinary((long)GetBlittable(typeof(long))); case TypeCode.String: return StringHelper.GetString(ptr, charSet, sz); default: throw new NotSupportedException("Unsupported type parameter."); } object GetBlittable(Type retType) => InteropExtensions.GetValueType(ptr, retType); } /*public static IntPtr Convert(object value, Func memAlloc, out int bytesAllocated, CharSet charSet = CharSet.Auto, int prefixBytes = 0) { bytesAllocated = 0; if (value is null) return IntPtr.Zero; var type = value.GetType(); // Handle special cases if (type.IsArray || type.InheritsFrom(typeof(IEnumerable<>))) { var elemType = type.FindElementType(); if (elemType == typeof(string)) { } else { var enumType = typeof(IEnumerable<>).MakeGenericType(new[] { elemType }); var mi = typeof(InteropExtensions).GetMethod("Write", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(IntPtr), enumType, typeof(int), typeof(SizeT) }, null); var gmi = mi.MakeGenericMethod(new[] { elemType }); return gmi.Invoke(null, new object[] { x }); } } if (value is IEnumerable) { if () { } } throw new NotImplementedException(); }*/ /// Converts the specified pointer to . /// The destination type. /// A block of allocated memory. /// The character set. /// A value of the type specified. public static T ToType(this SafeAllocatedMemoryHandle hMem, CharSet charSet = CharSet.Auto) { if (hMem == null) throw new ArgumentNullException(nameof(hMem)); hMem.Lock(); try { return Convert(hMem.DangerousGetHandle(), hMem.Size, charSet); } finally { hMem.Unlock(); } } } }