diff --git a/Core/Extensions/InteropExtensions.cs b/Core/Extensions/InteropExtensions.cs index a77578be..65e4e32a 100644 --- a/Core/Extensions/InteropExtensions.cs +++ b/Core/Extensions/InteropExtensions.cs @@ -173,8 +173,8 @@ namespace Vanara.Extensions /// /// 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 . + /// 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 . [Obsolete("Please use the Vanara.Extensions.InteropExtensions.Write method instead. This will be removed from the library shortly as it performs no allocation.", true)] @@ -540,7 +540,10 @@ namespace Vanara.Extensions } if (i + charLength > allocatedBytes) throw new InsufficientMemoryException(); - int GetCh(IntPtr p) => charLength == 1 ? Marshal.ReadByte(p) : Marshal.ReadInt16(p); + int GetCh(IntPtr p) + { + return charLength == 1 ? Marshal.ReadByte(p) : Marshal.ReadInt16(p); + } } /// @@ -576,7 +579,7 @@ namespace Vanara.Extensions if (allocatedBytes > 0 && allocatedBytes < stSize + offset) throw new InsufficientMemoryException(); if (t == typeof(T)) - Marshal.PtrToStructure(ptr, (object)instance); + Marshal.PtrToStructure(ptr, instance); else using (var pin = new PinnedObject(instance)) ((IntPtr)pin).Write(Marshal.PtrToStructure(ptr.Offset(offset), t)); @@ -598,22 +601,25 @@ namespace Vanara.Extensions unsafe { return new UIntPtr(p.ToPointer()); } } - /// - /// 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. - /// 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 . + /// 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. + /// + /// + /// 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 enumerated list of items to marshal. - /// The number of bytes to skip before writing the first element of . - /// If known, the total number of bytes allocated to the native memory in . + /// The number of bytes to skip before writing the first element of . + /// If known, the total number of bytes allocated to the native memory in . + /// The number of bytes written. The offset is not included. /// Structure layout is not sequential or explicit. /// - public static void Write(this IntPtr ptr, IEnumerable items, int offset = 0, SizeT allocatedBytes = default) + public static int Write(this IntPtr ptr, IEnumerable items, int offset = 0, SizeT allocatedBytes = default) { var count = items?.Count() ?? 0; - if (count == 0) return; + if (count == 0) return 0; if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit."); @@ -626,18 +632,16 @@ namespace Vanara.Extensions var i = 0; foreach (var item in items.Select(v => Convert.ChangeType(v, ttype))) Marshal.StructureToPtr(item, ptr.Offset(offset + i++ * stSize), false); + + return bytesReq - offset; } - /// - /// Writes the specified value to pre-allocated memory. - /// + /// Writes the specified value to pre-allocated memory. /// The address of the memory where the value is to be written. /// The value to write. - /// The number of bytes to offset from before writing. - /// If known, the total number of bytes allocated to the native memory in . - /// - /// The number of bytes written. The offset is not included. - /// + /// The number of bytes to offset from before writing. + /// If known, the total number of bytes allocated to the native memory in . + /// The number of bytes written. The offset is not included. /// public static int Write(this IntPtr ptr, object value, int offset = 0, SizeT allocatedBytes = default) { @@ -658,8 +662,15 @@ namespace Vanara.Extensions public static int Write(this IntPtr ptr, T value, int offset = 0, SizeT allocatedBytes = default) where T : struct => WriteNoChecks(ptr, value, offset, allocatedBytes); + internal static Type TrueType(Type type, out int size) + { + var ttype = type.IsEnum ? Enum.GetUnderlyingType(type) : type; + size = Marshal.SizeOf(ttype); + return ttype; + } + private static T GetValueType(IntPtr ptr, Type trueType = null, int offset = 0) => - (T)GetValueType(ptr, typeof(T), trueType, offset); + (T)GetValueType(ptr, typeof(T), trueType, offset); private static object GetValueType(IntPtr ptr, Type type, Type trueType = null, int offset = 0) { @@ -673,13 +684,6 @@ namespace Vanara.Extensions return Convert.ChangeType(obj, type); } - private static Type TrueType(Type type, out int size) - { - var ttype = type.IsEnum ? Enum.GetUnderlyingType(type) : type; - size = Marshal.SizeOf(ttype); - return ttype; - } - private static object TrueValue(object value, out int size) => Convert.ChangeType(value, TrueType(value.GetType(), out size)); private static int WriteNoChecks(IntPtr ptr, object value, int offset, SizeT allocatedBytes) diff --git a/Core/InteropServices/SafeMemoryHandle.cs b/Core/InteropServices/SafeMemoryHandle.cs index d45b5e2d..d410d633 100644 --- a/Core/InteropServices/SafeMemoryHandle.cs +++ b/Core/InteropServices/SafeMemoryHandle.cs @@ -116,8 +116,9 @@ namespace Vanara.InteropServices /// Marshals data from this 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. + /// Number of bytes preceding the structure. /// A managed object that contains the data that this holds. - T ToStructure(); + T ToStructure(int prefixBytes = 0); } /// Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory. @@ -327,7 +328,7 @@ namespace Vanara.InteropServices // throw new InsufficientMemoryException("Requested array is larger than the memory allocated."); if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit."); Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); - return handle.ToArray(count, prefixBytes); + return handle.ToArray(count, prefixBytes, sz); } /// Extracts an enumeration of structures of containing items. @@ -344,21 +345,21 @@ namespace Vanara.InteropServices // throw new InsufficientMemoryException("Requested array is larger than the memory allocated."); if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit."); Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); - return handle.ToIEnum(count, prefixBytes); + return handle.ToIEnum(count, prefixBytes, sz); } /// Returns a that represents this instance. /// The length. /// The character set of the string. /// A that represents this instance. - public string ToString(int len, CharSet charSet = CharSet.Unicode) => len == -1 ? StringHelper.GetString(handle, charSet) : StringHelper.GetString(handle, charSet).Substring(0, len); + public string ToString(int len, CharSet charSet = CharSet.Unicode) => len == -1 ? StringHelper.GetString(handle, charSet) : StringHelper.GetString(handle, charSet, sz).Substring(0, len); /// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length . /// The count of expected strings. /// The character set of the strings. /// Number of bytes preceding the array of string pointers. /// Enumeration of strings. - public IEnumerable ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(count, charSet, prefixBytes); + public IEnumerable ToStringEnum(int count, CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(count, charSet, prefixBytes, sz); /// /// 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 @@ -367,17 +368,57 @@ namespace Vanara.InteropServices /// The character set of the strings. /// Number of bytes preceding the array of string pointers. /// An enumerated list of strings. - public IEnumerable ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(charSet, prefixBytes); + public IEnumerable ToStringEnum(CharSet charSet = CharSet.Auto, int prefixBytes = 0) => IsInvalid ? new string[0] : handle.ToStringEnum(charSet, prefixBytes, sz); /// Marshals data from this 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. + /// Number of bytes preceding the structure. /// A managed object that contains the data that this holds. - public T ToStructure() + public T ToStructure(int prefixBytes = 0) { if (IsInvalid) return default; - //if (Size < Marshal.SizeOf(typeof(T))) - // throw new InsufficientMemoryException("Requested structure is larger than the memory allocated."); - return handle.ToStructure(); + return handle.ToStructure(sz, prefixBytes); + } + + /// + /// Marshals data from a managed list of specified type to an offset within this allocated 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. + /// if set to true automatically extend the allocated memory to the size required to hold . + /// The number of bytes to skip before writing the first element of . + public void Write(IEnumerable items, bool autoExtend = true, int offset = 0) + { + if (IsInvalid) throw new MemberAccessException("Safe memory pointer is not valid."); + if (autoExtend) + { + var count = items?.Count() ?? 0; + if (count == 0) return; + InteropExtensions.TrueType(typeof(T), out var iSz); + var reqSz = iSz * count + offset; + if (sz < reqSz) + Size = reqSz; + } + handle.Write(items, offset, sz); + } + + /// Writes the specified value to an offset within this allocated memory. + /// The type of the value to write. + /// The value to write. + /// if set to true automatically extend the allocated memory to the size required to hold . + /// The number of bytes to offset from the beginning of this allocated memory before writing. + public void Write(T value, bool autoExtend = true, int offset = 0) where T : struct + { + if (IsInvalid) throw new MemberAccessException("Safe memory pointer is not valid."); + if (autoExtend) + { + InteropExtensions.TrueType(typeof(T), out var iSz); + var reqSz = iSz + offset; + if (sz < reqSz) + Size = reqSz; + } + handle.Write(value, offset, sz); } /// When overridden in a derived class, executes the code required to free the handle.