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);
}
}
}