using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Vanara.Extensions; namespace Vanara.InteropServices { /// A safe class that represents an object that is pinned in memory. /// public class PinnedObject : IDisposable { private readonly int mOffset; private GCHandle pinnedArray; /// Initializes a new instance of the class. /// The object to pin. /// The offset into the pinned bytes used to return a pointer. public PinnedObject(object obj, int offset = 0) { mOffset = offset; SetObject(obj); } /// Creates a pinned object from a nullable structure. /// The type of the structure. /// The value to pin. /// The offset into the pinned bytes used to return a pointer. /// A new instance of . public static PinnedObject FromNullable(T? obj, int offset = 0) where T : struct => obj.HasValue ? new PinnedObject(obj.Value, offset) : new PinnedObject(null, offset); /// Initializes a new instance of the class. [ExcludeFromCodeCoverage] protected PinnedObject() { } /// Gets a value indicating whether the object is no longer pinned. /// true if the object is no longer pinned; otherwise, false. public bool IsInvalid => !pinnedArray.IsAllocated; /// Get a pointer ( ) to the pinned memory of the object with any preset offset. /// The instance. /// The result of the conversion. public static implicit operator IntPtr(PinnedObject ap) => !ap.IsInvalid ? ap.pinnedArray.AddrOfPinnedObject().Offset(ap.mOffset) : IntPtr.Zero; /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() { if (pinnedArray.IsAllocated) pinnedArray.Free(); } /// Sets the object. This should only be called once per instance in the constructor. /// The object to pin. protected void SetObject(object obj) { if (obj != null) pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned); } } }