using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.PInvoke; #if ALLOWSPAN using System.Buffers; #endif namespace Vanara.InteropServices { /// Base abstract class for a structure handler based on . /// The type of the structure. /// The type of the memory. /// public abstract class SafeMemStruct : SafeMemoryHandle, IEquatable where TMem : IMemoryMethods, new() where TStruct : struct { /// The structure size, in bytes, of TStruct. protected static readonly SizeT BaseStructSize = InteropExtensions.SizeOf(); /// Initializes a new instance of the class. /// The TStruct value. /// The capacity of the buffer, in bytes. protected SafeMemStruct(in TStruct s, SizeT capacity = default) : base(Math.Max(BaseStructSize, (ulong)capacity)) => handle.Write(s); /// Initializes a new instance of the class. /// The capacity of the buffer, in bytes. protected SafeMemStruct(SizeT capacity = default) : base(Math.Max(BaseStructSize, (ulong)capacity)) { } /// Initializes a new instance of the class. /// The PTR. /// true to reliably release the handle during finalization; false to prevent it. /// The number of bytes allocated to . [ExcludeFromCodeCoverage] protected SafeMemStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, allocatedBytes, ownsHandle) { } /// Gets a value indicating whether the current memory has a valid value of its underlying type. /// if this instance has a value; otherwise, . public bool HasValue => !IsClosed && !IsInvalid; /// /// Gets or sets the value of the current object if it has been assigned a valid /// underlying value. /// /// /// The value of the current object if the HasValue property is true. An exception is /// thrown if the HasValue property is false. /// /// The HasValue property is false. public TStruct Value { get => HasValue ? handle.ToStructure(Size) : throw new InvalidOperationException("The HasValue property is false."); set => _ = HasValue ? handle.Write(value, 0, Size) : throw new InvalidOperationException("The HasValue property is false."); } /// Returns the TStruct value held by a . /// The instance. /// /// A nullable value held by the or null if the handle or value is invalid. /// public static explicit operator TStruct?(SafeMemStruct s) => s is null || !s.HasValue ? (TStruct?)null : s.Value; /// Performs an explicit conversion from to . /// The instance. /// The result of the conversion. /// Cannot convert an ANSI string to a Char pointer. public static unsafe explicit operator void*(SafeMemStruct s) => (void*)s.handle; /// Returns the value of the field. /// The instance. /// /// An representing the value of the handle field. If the handle has been marked invalid with , this method still returns the original handle value, which can be a stale value. /// public static implicit operator IntPtr(SafeMemStruct s) => s.DangerousGetHandle(); /// Returns the TStruct value held by a . /// The instance. /// /// The structure value held by the or an /// exception if the handle or value is invalid. /// public static implicit operator TStruct(SafeMemStruct s) => s is not null ? s.Value : throw new ArgumentNullException(nameof(s)); /// Appends the specified bytes to the end of the allocated memory for this structure, expanding the allocation to fit the byte array. /// The bytes. /// A pointer to the copied bytes in memory. public virtual IntPtr Append(byte[] bytes) { var sz = Size; Size += bytes.Length; Marshal.Copy(bytes, 0, handle.Offset(sz), bytes.Length); return handle.Offset(sz); } /// Appends the specified memory to the end of the allocated memory for this structure, expanding the allocation to fit the added memory. /// The memory to append. /// A pointer to the copied memory. public virtual IntPtr Append(SafeAllocatedMemoryHandleBase mem) { var sz = Size; Size += mem.Size; ((IntPtr)mem).CopyTo(handle.Offset(sz), mem.Size); return handle.Offset(sz); } /// Appends the specified object to the end of the allocated memory for this structure, expanding the allocation to fit the added object. /// The value to append. /// A pointer to the copied memory. public virtual IntPtr Append(object value) { var sz = Size; var vSz = InteropExtensions.SizeOf(value); Size += vSz; handle.Write(value, sz, Size); return handle.Offset(sz); } /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj switch { null => false, SafeMemStruct ms => Equals((TStruct?)this, (TStruct?)ms), TStruct s => Equals(s), SafeAllocatedMemoryHandle m => m.DangerousGetHandle() == handle, _ => false, }; /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(TStruct other) => HasValue && EqualityComparer.Default.Equals(handle.ToStructure(Size), other); /// Gets the memory address of a field within . /// Name of the field. /// The pointer to the field in memory. public virtual IntPtr GetFieldAddress(string fieldName) => handle.Offset(FieldOffset(fieldName)); /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => handle.ToInt32(); /// Retrieves the value of the current object, or the specified default value. /// A value to return if the property is . /// /// The value of the property if the property is ; otherwise, the /// parameter. /// public virtual TStruct GetValueOrDefault(in TStruct defaultValue = default) => HasValue ? Value : defaultValue; /// Initializes the size field by the specified name or the first four bytes of the structure's memory. /// Name of the field. public virtual void InitializeSizeField(string fieldName = null) => (fieldName is null ? handle : GetFieldAddress(fieldName)).Write((uint)BaseStructSize); /// Returns the string value held by this instance. /// A value held by this instance or null if the handle is invalid. public override string ToString() => ((TStruct?)this).ToString(); /// Returns the field offset of the named field. /// The field name. /// The offset, in bytes, of the field within the structure. protected static long FieldOffset(string name) => Marshal.OffsetOf(typeof(TStruct), name).ToInt64(); #if ALLOWSPAN /// Gets a reference to a structure based on this allocated memory. /// A referenced structure. public ref TStruct AsRef() => ref MemoryMarshal.GetReference(AsSpan()); /// Creates a new span over this allocated memory. /// The span representation of the structure. public Span AsSpan() => base.AsSpan(1); #endif } /// A structure handler based on unmanaged memory allocated by AllocCoTaskMem. /// The type of the structure. /// public class SafeCoTaskMemStruct : SafeMemStruct where TStruct : struct { /// Initializes a new instance of the class. /// The TStruct value. /// The capacity of the buffer, in bytes. public SafeCoTaskMemStruct(in TStruct s, SizeT capacity = default) : base(s, capacity) { } /// Initializes a new instance of the class. /// The capacity of the buffer, in bytes. public SafeCoTaskMemStruct(SizeT capacity = default) : base(capacity) { } /// Initializes a new instance of the class. /// The PTR. /// true to reliably release the handle during finalization; false to prevent it. /// The number of bytes allocated to . [ExcludeFromCodeCoverage] public SafeCoTaskMemStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, ownsHandle, allocatedBytes) { } /// Represents the equivalent of this class instances. public static readonly SafeCoTaskMemStruct Null = new(IntPtr.Zero, false); /// Performs an implicit conversion from to . /// The value of the instance or . /// The resulting instance from the conversion. public static implicit operator SafeCoTaskMemStruct(TStruct? s) => s.HasValue ? new SafeCoTaskMemStruct(s.Value) : new SafeCoTaskMemStruct(IntPtr.Zero); } /// A structure handler based on unmanaged memory allocated by AllocHGlobal. /// The type of the structure. /// public class SafeHGlobalStruct : SafeMemStruct where TStruct : struct { /// Initializes a new instance of the class. /// The TStruct value. /// The capacity of the buffer, in bytes. public SafeHGlobalStruct(in TStruct s, SizeT capacity = default) : base(s, capacity) { } /// Initializes a new instance of the class. /// The capacity of the buffer, in bytes. public SafeHGlobalStruct(SizeT capacity = default) : base(capacity) { } /// Initializes a new instance of the class. /// The PTR. /// true to reliably release the handle during finalization; false to prevent it. /// The number of bytes allocated to . [ExcludeFromCodeCoverage] public SafeHGlobalStruct(IntPtr ptr, bool ownsHandle = true, SizeT allocatedBytes = default) : base(ptr, ownsHandle, allocatedBytes) { } /// Represents the equivalent of this class instances. public static readonly SafeHGlobalStruct Null = new(IntPtr.Zero, false); /// Performs an implicit conversion from to . /// The value of the instance or . /// The resulting instance from the conversion. public static implicit operator SafeHGlobalStruct(TStruct? s) => s.HasValue ? new SafeHGlobalStruct(s.Value) : new SafeHGlobalStruct(IntPtr.Zero); } }