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 null) ? s.Value : throw new ArgumentNullException(nameof(s)); /// 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) ? true : (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 ? false : EqualityComparer.Default.Equals(handle.ToStructure(Size), other); /// 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; /// 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) { } /// 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) { } /// 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); } }