2017-11-27 12:18:01 -05:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Diagnostics.CodeAnalysis ;
using System.Linq ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
using System.Security ;
using Vanara.Extensions ;
2019-07-25 21:46:45 -04:00
using Vanara.PInvoke ;
2017-11-27 12:18:01 -05:00
namespace Vanara.InteropServices
{
/// <summary>Method used to pack a list of strings into memory.</summary>
public enum StringListPackMethod
{
/// <summary>Each string is separated by a single '\0' character and is terminated by two '\0' characters.</summary>
Concatenated ,
2020-04-07 16:34:28 -04:00
/// <summary>
/// A contiguous block of memory containing an array of pointers to strings followed by a NULL pointer and then followed by the
/// actual strings.
/// </summary>
2017-11-27 12:18:01 -05:00
Packed
}
/// <summary>Interface to capture unmanaged memory methods.</summary>
2020-04-07 16:34:28 -04:00
public interface IMemoryMethods : ISimpleMemoryMethods
2017-11-27 12:18:01 -05:00
{
/// <summary>Gets the Ansi <see cref="SecureString"/> allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="secureString">The secure string.</param>
/// <returns>A memory handle.</returns>
IntPtr AllocSecureStringAnsi ( SecureString secureString ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the Unicode <see cref="SecureString"/> allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="secureString">The secure string.</param>
/// <returns>A memory handle.</returns>
IntPtr AllocSecureStringUni ( SecureString secureString ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the Ansi string allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="value">The value.</param>
/// <returns>A memory handle.</returns>
IntPtr AllocStringAnsi ( string value ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the Unicode string allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="value">The value.</param>
/// <returns>A memory handle.</returns>
IntPtr AllocStringUni ( string value ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the Ansi <see cref="SecureString"/> free method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="hMem">A memory handle.</param>
void FreeSecureStringAnsi ( IntPtr hMem ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the Unicode <see cref="SecureString"/> free method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="hMem">A memory handle.</param>
void FreeSecureStringUni ( IntPtr hMem ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Gets the reallocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="hMem">A memory handle.</param>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>A memory handle.</returns>
IntPtr ReAllocMem ( IntPtr hMem , int size ) ;
2017-11-27 12:18:01 -05:00
}
/// <summary>Interface for classes that support safe memory pointers.</summary>
2019-09-19 14:22:21 -04:00
public interface ISafeMemoryHandle : IDisposable
2017-11-27 12:18:01 -05:00
{
/// <summary>Gets a value indicating whether the handle value is invalid.</summary>
bool IsInvalid { get ; }
/// <summary>Gets the size of the allocated memory block.</summary>
/// <value>The size of the allocated memory block.</value>
2019-07-25 21:46:45 -04:00
SizeT Size { get ; set ; }
2017-11-27 12:18:01 -05:00
/// <summary>
2020-04-07 16:34:28 -04:00
/// Adds reference to other SafeMemoryHandle objects, the pointer to which are referred to by this object. This is to ensure that
/// such objects being referred to wouldn't be unreferenced until this object is active. For e.g. when this object is an array of
/// pointers to other objects
2017-11-27 12:18:01 -05:00
/// </summary>
/// <param name="children">Collection of SafeMemoryHandle objects referred to by this object.</param>
void AddSubReference ( IEnumerable < ISafeMemoryHandle > children ) ;
/// <summary>Returns the instance as an <see cref="IntPtr"/>. This is a dangerous call as the value is mutable.</summary>
/// <returns>An <see cref="IntPtr"/> to the internally held memory.</returns>
IntPtr DangerousGetHandle ( ) ;
2021-01-11 13:19:15 -05:00
/// <summary>Gets a copy of bytes from the allocated memory block.</summary>
/// <param name="startIndex">The start index.</param>
/// <param name="count">The number of bytes to retrieve.</param>
/// <returns>A byte array with the copied bytes.</returns>
public byte [ ] GetBytes ( int startIndex , int count ) ;
2017-11-27 12:18:01 -05:00
/// <summary>
2020-04-07 16:34:28 -04:00
/// Extracts an array of structures of <typeparamref name="T"/> containing <paramref name="count"/> items. <note type="note">This
/// call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.</note>
2017-11-27 12:18:01 -05:00
/// </summary>
/// <typeparam name="T">The type of the structures to retrieve.</typeparam>
/// <param name="count">The number of structures to retrieve.</param>
/// <param name="prefixBytes">The number of bytes to skip before reading the structures.</param>
/// <returns>An array of structures of <typeparamref name="T"/>.</returns>
T [ ] ToArray < T > ( int count , int prefixBytes = 0 ) ;
/// <summary>
2020-04-07 16:34:28 -04:00
/// Extracts an enumeration of structures of <typeparamref name="T"/> containing <paramref name="count"/> items. <note
/// type="note">This call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.</note>
2017-11-27 12:18:01 -05:00
/// </summary>
/// <typeparam name="T">The type of the structures to retrieve.</typeparam>
/// <param name="count">The number of structures to retrieve.</param>
/// <param name="prefixBytes">The number of bytes to skip before reading the structures.</param>
/// <returns>An enumeration of structures of <typeparamref name="T"/>.</returns>
IEnumerable < T > ToEnumerable < T > ( int count , int prefixBytes = 0 ) ;
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <param name="len">The length.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
string ToString ( int len , CharSet charSet = CharSet . Unicode ) ;
2019-09-27 15:32:19 -04:00
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <param name="len">The length.</param>
/// <param name="prefixBytes">Number of bytes preceding the string pointer.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
string ToString ( int len , int prefixBytes , CharSet charSet = CharSet . Unicode ) ;
2017-11-27 12:18:01 -05:00
/// <summary>
2020-04-07 16:34:28 -04:00
/// 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 two '\0' characters.
2017-11-27 12:18:01 -05:00
/// </summary>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>Enumeration of strings.</returns>
IEnumerable < string > ToStringEnum ( CharSet charSet = CharSet . Auto , int prefixBytes = 0 ) ;
2020-04-07 16:34:28 -04:00
/// <summary>
/// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length
/// <paramref name="count"/>.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="count">The count.</param>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>An enumerated list of strings.</returns>
IEnumerable < string > ToStringEnum ( int count , CharSet charSet = CharSet . Auto , int prefixBytes = 0 ) ;
2020-04-07 16:34:28 -04:00
/// <summary>
/// Marshals data from this block of memory to a newly allocated managed object of the type specified by a generic type parameter.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="T">The type of the object to which the data is to be copied. This must be a structure.</typeparam>
2019-08-21 12:18:07 -04:00
/// <param name="prefixBytes">Number of bytes preceding the structure.</param>
2017-11-27 12:18:01 -05:00
/// <returns>A managed object that contains the data that this <see cref="SafeMemoryHandleExt{T}"/> holds.</returns>
2019-08-21 12:18:07 -04:00
T ToStructure < T > ( int prefixBytes = 0 ) ;
2017-11-27 12:18:01 -05:00
}
2020-04-07 16:34:28 -04:00
/// <summary>Interface to capture unmanaged simple (alloc/free) memory methods.</summary>
public interface ISimpleMemoryMethods
{
2022-01-16 19:22:34 -05:00
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
bool Lockable { get ; }
2020-07-20 11:48:32 -04:00
/// <summary>Gets a handle to a memory allocation of the specified size.</summary>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>A memory handle.</returns>
IntPtr AllocMem ( int size ) ;
/// <summary>Frees the memory associated with a handle.</summary>
/// <param name="hMem">A memory handle.</param>
void FreeMem ( IntPtr hMem ) ;
/// <summary>Locks the memory of a specified handle and gets a pointer to it.</summary>
/// <param name="hMem">A memory handle.</param>
/// <returns>A pointer to the locked memory.</returns>
IntPtr LockMem ( IntPtr hMem ) ;
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
2022-01-16 19:22:34 -05:00
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
bool UnlockMem ( IntPtr hMem ) ;
2020-04-07 16:34:28 -04:00
}
/// <summary>Implementation of <see cref="IMemoryMethods"/> using just the methods from <see cref="ISimpleMemoryMethods"/>.</summary>
/// <seealso cref="Vanara.InteropServices.IMemoryMethods"/>
2020-07-20 11:48:32 -04:00
public abstract class MemoryMethodsBase : IMemoryMethods
2020-04-07 16:34:28 -04:00
{
2022-01-16 19:22:34 -05:00
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public virtual bool Lockable = > false ;
2020-07-20 11:48:32 -04:00
/// <summary>
/// Gets a handle to a memory allocation of the specified size.
/// </summary>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>
/// A memory handle.
/// </returns>
/// <exception cref="NotImplementedException"></exception>
public abstract IntPtr AllocMem ( int size ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Ansi <see cref="SecureString"/> allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="secureString">The secure string.</param>
/// <returns>A memory handle.</returns>
public virtual IntPtr AllocSecureStringAnsi ( SecureString secureString ) = > StringHelper . AllocSecureString ( secureString , CharSet . Ansi , AllocMem ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Unicode <see cref="SecureString"/> allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="secureString">The secure string.</param>
/// <returns>A memory handle.</returns>
public virtual IntPtr AllocSecureStringUni ( SecureString secureString ) = > StringHelper . AllocSecureString ( secureString , CharSet . Unicode , AllocMem ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Ansi string allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="value">The value.</param>
/// <returns>A memory handle.</returns>
public virtual IntPtr AllocStringAnsi ( string value ) = > StringHelper . AllocString ( value , CharSet . Ansi , AllocMem ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Unicode string allocation method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="value">The value.</param>
/// <returns>A memory handle.</returns>
public virtual IntPtr AllocStringUni ( string value ) = > StringHelper . AllocString ( value , CharSet . Unicode , AllocMem ) ;
/// <summary>Frees the memory associated with a handle.</summary>
/// <param name="hMem">A memory handle.</param>
public abstract void FreeMem ( IntPtr hMem ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Ansi <see cref="SecureString"/> free method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="hMem">A memory handle.</param>
public virtual void FreeSecureStringAnsi ( IntPtr hMem ) = > FreeMem ( hMem ) ;
2020-04-07 16:34:28 -04:00
/// <summary>Gets the Unicode <see cref="SecureString"/> free method.</summary>
2020-07-20 11:48:32 -04:00
/// <param name="hMem">A memory handle.</param>
public virtual void FreeSecureStringUni ( IntPtr hMem ) = > FreeMem ( hMem ) ;
2020-04-07 16:34:28 -04:00
2020-07-20 11:48:32 -04:00
/// <summary>Locks the memory of a specified handle and gets a pointer to it.</summary>
/// <param name="hMem">A memory handle.</param>
/// <returns>A pointer to the locked memory.</returns>
public virtual IntPtr LockMem ( IntPtr hMem ) = > hMem ;
2020-04-07 16:34:28 -04:00
2020-07-20 11:48:32 -04:00
/// <summary>Gets the reallocation method.</summary>
/// <param name="hMem">A memory handle.</param>
/// <param name="size">The size, in bytes, of memory to allocate.</param>
/// <returns>A memory handle.</returns>
public virtual IntPtr ReAllocMem ( IntPtr hMem , int size )
{
var hNew = AllocMem ( size ) ;
hMem . CopyTo ( hNew , size ) ;
FreeMem ( hMem ) ;
return hNew ;
}
/// <summary>Unlocks the memory of a specified handle.</summary>
/// <param name="hMem">A memory handle.</param>
2022-01-16 19:22:34 -05:00
public virtual bool UnlockMem ( IntPtr hMem ) = > false ;
2020-04-07 16:34:28 -04:00
}
2020-12-22 18:26:27 -05:00
/// <summary>
/// Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory. This class assumes read-only memory.
/// </summary>
2018-01-06 18:36:28 -05:00
/// <seealso cref="System.Runtime.InteropServices.SafeHandle"/>
2020-12-22 18:26:27 -05:00
public abstract class SafeAllocatedMemoryHandleBase : SafeHandle
2018-01-06 18:36:28 -05:00
{
2020-12-22 18:26:27 -05:00
/// <summary>Initializes a new instance of the <see cref="SafeAllocatedMemoryHandleBase"/> class.</summary>
2018-01-06 18:36:28 -05:00
/// <param name="handle">The handle.</param>
/// <param name="ownsHandle">if set to <c>true</c> if this class is responsible for freeing the memory on disposal.</param>
2020-12-22 18:26:27 -05:00
protected SafeAllocatedMemoryHandleBase ( IntPtr handle , bool ownsHandle ) : base ( IntPtr . Zero , ownsHandle ) = > SetHandle ( handle ) ;
2018-01-06 18:36:28 -05:00
2020-05-14 19:53:35 -04:00
#if DEBUG
2020-04-07 16:34:28 -04:00
/// <summary>Dumps memory to byte string.</summary>
[ExcludeFromCodeCoverage]
public string Dump = > Size = = 0 ? "" : string . Join ( " " , GetBytes ( 0 , Size ) . Select ( b = > b . ToString ( "X2" ) ) . ToArray ( ) ) ;
2020-05-14 19:53:35 -04:00
# endif
2020-04-07 16:34:28 -04:00
2022-01-16 19:22:34 -05:00
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public virtual bool Lockable = > false ;
2018-01-06 18:36:28 -05:00
/// <summary>Gets or sets the size in bytes of the allocated memory block.</summary>
/// <value>The size in bytes of the allocated memory block.</value>
2019-07-25 21:46:45 -04:00
public abstract SizeT Size { get ; set ; }
2018-01-06 18:36:28 -05:00
2020-12-22 18:26:27 -05:00
/// <summary>Performs an explicit conversion from <see cref="SafeAllocatedMemoryHandleBase"/> to <see cref="byte"/> pointer.</summary>
/// <param name="hMem">The <see cref="SafeAllocatedMemoryHandleBase"/> instance.</param>
2020-04-07 16:34:28 -04:00
/// <returns>The result of the conversion.</returns>
2020-12-22 18:26:27 -05:00
public static unsafe explicit operator byte * ( SafeAllocatedMemoryHandleBase hMem ) = > ( byte * ) hMem . handle ;
2020-04-07 16:34:28 -04:00
2020-12-22 18:26:27 -05:00
/// <summary>Performs an explicit conversion from <see cref="SafeAllocatedMemoryHandleBase"/> to <see cref="SafeBuffer"/>.</summary>
/// <param name="hMem">The <see cref="SafeAllocatedMemoryHandleBase"/> instance.</param>
2020-04-07 16:34:28 -04:00
/// <returns>The result of the conversion.</returns>
2020-12-22 18:26:27 -05:00
public static explicit operator SafeBuffer ( SafeAllocatedMemoryHandleBase hMem ) = > new SafeBufferImpl ( hMem ) ;
2020-04-07 16:34:28 -04:00
2020-12-22 18:26:27 -05:00
/// <summary>Performs an implicit conversion from <see cref="SafeAllocatedMemoryHandleBase"/> to <see cref="System.IntPtr"/>.</summary>
/// <param name="hMem">The <see cref="SafeAllocatedMemoryHandleBase"/> instance.</param>
2020-04-07 16:34:28 -04:00
/// <returns>The result of the conversion.</returns>
2020-12-22 18:26:27 -05:00
public static implicit operator IntPtr ( SafeAllocatedMemoryHandleBase hMem ) = > hMem . handle ;
2020-04-07 16:34:28 -04:00
2020-05-14 19:53:35 -04:00
#if ALLOWSPAN
/// <summary>Creates a new span over this allocated memory.</summary>
/// <returns>The span representation of the structure.</returns>
2020-05-16 10:56:21 -04:00
public virtual ReadOnlySpan < T > AsReadOnlySpan < T > ( int length ) = > handle . AsReadOnlySpan < T > ( length , 0 , Size ) ;
/// <summary>Creates a new span over this allocated memory.</summary>
/// <returns>The span representation of the structure.</returns>
public virtual Span < T > AsSpan < T > ( int length ) = > handle . AsSpan < T > ( length , 0 , Size ) ;
2020-05-14 19:53:35 -04:00
/// <summary>Casts this allocated memory to a <c>Span<Byte></c>.</summary>
/// <returns>A span of type <see cref="byte"/>.</returns>
public virtual Span < byte > AsBytes ( ) = > AsSpan < byte > ( Size ) ;
# endif
2022-01-16 19:22:34 -05:00
/// <summary>Locks this instance.</summary>
public virtual void Lock ( )
{
}
/// <summary>Decrements the lock count.</summary>
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public virtual bool Unlock ( ) = > false ;
2019-10-29 12:44:02 -04:00
/// <summary>Releases the owned handle without releasing the allocated memory and returns a pointer to the current memory.</summary>
/// <returns>A pointer to the currently allocated memory. The caller now has the responsibility to free this memory.</returns>
public virtual IntPtr TakeOwnership ( )
{
2022-01-16 19:22:34 -05:00
while ( Unlock ( ) ) ;
2019-10-29 12:44:02 -04:00
var h = handle ;
SetHandleAsInvalid ( ) ;
handle = IntPtr . Zero ;
Size = 0 ;
return h ;
}
2022-01-22 17:30:42 -05:00
/// <summary>Gets a copy of bytes from the allocated memory block.</summary>
/// <returns>A byte array with the copied bytes.</returns>
public byte [ ] GetBytes ( ) = > GetBytes ( 0 , Size ) ;
2019-03-25 20:38:42 -04:00
/// <summary>Gets a copy of bytes from the allocated memory block.</summary>
/// <param name="startIndex">The start index.</param>
/// <param name="count">The number of bytes to retrieve.</param>
/// <returns>A byte array with the copied bytes.</returns>
2020-07-22 20:23:37 -04:00
public byte [ ] GetBytes ( int startIndex , int count )
2019-03-25 20:38:42 -04:00
{
2022-01-16 19:22:34 -05:00
if ( startIndex < 0 | | startIndex + count > Size ) throw new ArgumentOutOfRangeException ( nameof ( startIndex ) ) ;
2019-03-25 20:38:42 -04:00
var ret = new byte [ count ] ;
2022-01-16 19:22:34 -05:00
CallLocked ( p = > Marshal . Copy ( p . Offset ( startIndex ) , ret , 0 , count ) ) ;
2019-03-25 20:38:42 -04:00
return ret ;
}
2022-01-16 19:22:34 -05:00
/// <summary>Runs a delegate method while locking the memory.</summary>
/// <param name="action">The action to perform while memory is locked.</param>
protected void CallLocked ( Action < IntPtr > action )
{
if ( ! Lockable )
action . Invoke ( handle ) ;
else
{
try { Lock ( ) ; action . Invoke ( handle ) ; }
finally { Unlock ( ) ; }
}
}
/// <summary>Runs a delegate method while locking the memory.</summary>
/// <param name="action">The action to perform while memory is locked.</param>
protected TOut CallLocked < TOut > ( Func < IntPtr , TOut > action )
{
if ( ! Lockable )
return action . Invoke ( handle ) ;
try { Lock ( ) ; return action . Invoke ( handle ) ; }
finally { Unlock ( ) ; }
}
2019-03-25 20:38:42 -04:00
private class SafeBufferImpl : SafeBuffer
{
2020-12-22 18:26:27 -05:00
public SafeBufferImpl ( SafeAllocatedMemoryHandleBase hMem ) : base ( false ) = > Initialize ( ( ulong ) hMem . Size ) ;
2019-03-25 20:38:42 -04:00
protected override bool ReleaseHandle ( ) = > true ;
2018-01-06 18:36:28 -05:00
}
}
2020-12-22 18:26:27 -05:00
/// <summary>Abstract base class for all SafeHandle derivatives that encapsulate handling unmanaged memory.</summary>
/// <seealso cref="System.Runtime.InteropServices.SafeHandle"/>
public abstract class SafeAllocatedMemoryHandle : SafeAllocatedMemoryHandleBase
{
/// <summary>Initializes a new instance of the <see cref="SafeAllocatedMemoryHandle"/> class.</summary>
/// <param name="handle">The handle.</param>
/// <param name="ownsHandle">if set to <c>true</c> if this class is responsible for freeing the memory on disposal.</param>
protected SafeAllocatedMemoryHandle ( IntPtr handle , bool ownsHandle ) : base ( handle , ownsHandle ) { }
/// <summary>Fills the allocated memory with a specific byte value.</summary>
/// <param name="value">The byte value.</param>
public virtual void Fill ( byte value ) = > Fill ( value , Size ) ;
/// <summary>Fills the allocated memory with a specific byte value.</summary>
/// <param name="value">The byte value.</param>
/// <param name="length">The number of bytes in the block of memory to be filled.</param>
public virtual void Fill ( byte value , int length )
{
if ( length > Size ) throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
2022-01-16 19:22:34 -05:00
CallLocked ( p = > p . FillMemory ( value , length ) ) ;
2020-12-22 18:26:27 -05:00
}
/// <summary>Zero out all allocated memory.</summary>
public virtual void Zero ( ) = > Fill ( 0 , Size ) ;
}
2018-01-06 18:36:28 -05:00
/// <summary>Abstract base class for all SafeAllocatedMemoryHandle derivatives that apply a specific memory handling routine set.</summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="TMem">The <see cref="IMemoryMethods"/> implementation.</typeparam>
2018-01-06 18:36:28 -05:00
public abstract class SafeMemoryHandle < TMem > : SafeAllocatedMemoryHandle where TMem : IMemoryMethods , new ( )
2017-11-27 12:18:01 -05:00
{
/// <summary>The <see cref="IMemoryMethods"/> implementation instance.</summary>
2022-01-16 19:22:34 -05:00
protected static readonly TMem mm = new ( ) ;
2017-11-27 12:18:01 -05:00
/// <summary>The number of bytes currently allocated.</summary>
2019-07-25 21:46:45 -04:00
protected SizeT sz ;
2017-11-27 12:18:01 -05:00
2022-01-16 19:22:34 -05:00
private IntPtr unlockedHandle ;
2017-11-27 12:18:01 -05:00
/// <summary>Initializes a new instance of the <see cref="SafeMemoryHandle{T}"/> class.</summary>
/// <param name="size">The size of memory to allocate, in bytes.</param>
/// <exception cref="System.ArgumentOutOfRangeException">size - The value of this argument must be non-negative</exception>
2019-07-27 14:48:33 -04:00
protected SafeMemoryHandle ( SizeT size = default ) : base ( IntPtr . Zero , true )
2017-11-27 12:18:01 -05:00
{
if ( size = = 0 ) return ;
2019-10-31 13:47:04 -04:00
InitFromSize ( size ) ;
2019-04-08 13:54:24 -04:00
Zero ( ) ;
2017-11-27 12:18:01 -05:00
}
/// <summary>Initializes a new instance of the <see cref="SafeMemoryHandle{T}"/> class.</summary>
/// <param name="handle">The handle.</param>
/// <param name="size">The size of memory allocated to the handle, in bytes.</param>
/// <param name="ownsHandle">if set to <c>true</c> if this class is responsible for freeing the memory on disposal.</param>
2019-07-27 14:48:33 -04:00
protected SafeMemoryHandle ( IntPtr handle , SizeT size , bool ownsHandle ) : base ( handle , ownsHandle ) = > sz = size ;
2017-11-27 12:18:01 -05:00
2020-04-07 16:34:28 -04:00
/// <summary>
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="bytes">Array of unmanaged pointers</param>
/// <returns>SafeHGlobalHandle object to an native (unmanaged) array of pointers</returns>
2019-10-31 13:47:04 -04:00
protected SafeMemoryHandle ( byte [ ] bytes ) : base ( IntPtr . Zero , true )
{
if ( ( bytes ? . Length ? ? 0 ) = = 0 ) return ;
InitFromSize ( bytes . Length ) ;
2022-01-16 19:22:34 -05:00
CallLocked ( p = > Marshal . Copy ( bytes , 0 , p , bytes . Length ) ) ;
2019-10-31 13:47:04 -04:00
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeMemoryHandle{TMem}"/> class from a <see cref="SafeAllocatedMemoryHandle"/>
/// instance, copying all the memory.
/// </summary>
/// <param name="source">The source memory block.</param>
protected SafeMemoryHandle ( SafeAllocatedMemoryHandle source ) : base ( IntPtr . Zero , true )
{
if ( source is null ) return ;
InitFromSize ( source . Size ) ;
2022-01-16 19:22:34 -05:00
CallLocked ( p = > ( ( IntPtr ) source ) . CopyTo ( p , source . Size ) ) ;
2019-10-31 13:47:04 -04:00
}
2017-11-27 12:18:01 -05:00
/// <summary>When overridden in a derived class, gets a value indicating whether the handle value is invalid.</summary>
public override bool IsInvalid = > handle = = IntPtr . Zero ;
2022-01-22 17:30:42 -05:00
/// <summary>Gets a value indicating whether this memory supports locking.</summary>
/// <value><see langword="true"/> if lockable; otherwise, <see langword="false"/>.</value>
public override bool Lockable = > mm . Lockable ;
2018-01-06 18:36:28 -05:00
/// <summary>Gets or sets the size in bytes of the allocated memory block.</summary>
/// <value>The size in bytes of the allocated memory block.</value>
2019-07-25 21:46:45 -04:00
public override SizeT Size
2017-11-27 12:18:01 -05:00
{
get = > sz ;
set
{
if ( value = = 0 )
{
ReleaseHandle ( ) ;
}
else
{
2019-10-31 13:47:04 -04:00
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
2017-11-27 12:18:01 -05:00
handle = IsInvalid ? mm . AllocMem ( value ) : mm . ReAllocMem ( handle , value ) ;
2019-04-08 13:54:24 -04:00
if ( value > sz )
handle . Offset ( sz ) . FillMemory ( 0 , value - sz ) ;
2017-11-27 12:18:01 -05:00
sz = value ;
}
}
}
2022-01-16 19:22:34 -05:00
/// <summary>Locks this instance.</summary>
public override void Lock ( )
{
if ( ! Lockable ) return ;
if ( unlockedHandle = = default )
{
var hlocked = mm . LockMem ( handle ) ;
if ( hlocked ! = handle )
{
unlockedHandle = handle ;
SetHandle ( hlocked ) ;
}
}
else
mm . LockMem ( unlockedHandle ) ;
}
/// <summary>Decrements the lock count.</summary>
/// <returns><see langword="true"/> if the memory object is still locked after decrementing the lock count; otherwise <see langword="false"/>.</returns>
public override bool Unlock ( )
{
if ( ! Lockable | | unlockedHandle = = default )
return false ;
var stillLocked = mm . UnlockMem ( unlockedHandle ) ;
if ( ! stillLocked )
{
SetHandle ( unlockedHandle ) ;
unlockedHandle = default ;
}
return stillLocked ;
}
2017-11-27 12:18:01 -05:00
/// <summary>When overridden in a derived class, executes the code required to free the handle.</summary>
/// <returns>
2020-04-07 16:34:28 -04:00
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it
/// generates a releaseHandleFailed MDA Managed Debugging Assistant.
2017-11-27 12:18:01 -05:00
/// </returns>
protected override bool ReleaseHandle ( )
{
2022-01-16 19:22:34 -05:00
while ( Unlock ( ) ) ;
2017-11-27 12:18:01 -05:00
mm . FreeMem ( handle ) ;
sz = 0 ;
handle = IntPtr . Zero ;
return true ;
}
2019-10-31 13:47:04 -04:00
private void InitFromSize ( SizeT size )
{
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
SetHandle ( mm . AllocMem ( sz = size ) ) ;
}
2017-11-27 12:18:01 -05:00
}
/// <summary>A <see cref="SafeHandle"/> for memory allocated via COM.</summary>
/// <seealso cref="System.Runtime.InteropServices.SafeHandle"/>
public abstract class SafeMemoryHandleExt < TMem > : SafeMemoryHandle < TMem > , ISafeMemoryHandle where TMem : IMemoryMethods , new ( )
{
/// <summary>
2020-04-07 16:34:28 -04:00
/// Maintains reference to other SafeMemoryHandleExt objects, the pointer to which are referred to by this object. This is to ensure
/// that such objects being referred to wouldn't be unreferenced until this object is active.
2017-11-27 12:18:01 -05:00
/// </summary>
private List < ISafeMemoryHandle > references ;
/// <summary>Initializes a new instance of the <see cref="SafeMemoryHandleExt{T}"/> class.</summary>
/// <param name="size">The size of memory to allocate, in bytes.</param>
/// <exception cref="System.ArgumentOutOfRangeException">size - The value of this argument must be non-negative</exception>
2019-07-27 14:48:33 -04:00
protected SafeMemoryHandleExt ( SizeT size ) : base ( size ) { }
2017-11-27 12:18:01 -05:00
/// <summary>Initializes a new instance of the <see cref="SafeMemoryHandleExt{T}"/> class.</summary>
/// <param name="handle">The handle.</param>
/// <param name="size">The size of memory allocated to the handle, in bytes.</param>
/// <param name="ownsHandle">if set to <c>true</c> if this class is responsible for freeing the memory on disposal.</param>
2019-07-27 14:48:33 -04:00
protected SafeMemoryHandleExt ( IntPtr handle , SizeT size , bool ownsHandle ) : base ( handle , size , ownsHandle ) { }
2017-11-27 12:18:01 -05:00
2020-04-07 16:34:28 -04:00
/// <summary>
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="bytes">Array of unmanaged pointers</param>
/// <returns>SafeHGlobalHandle object to an native (unmanaged) array of pointers</returns>
protected SafeMemoryHandleExt ( byte [ ] bytes ) : base ( bytes ) { }
2020-04-07 16:34:28 -04:00
/// <summary>
/// Allocates from unmanaged memory to represent an array of pointers and marshals the unmanaged pointers (IntPtr) to the native
/// array equivalent.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="values">Array of unmanaged pointers</param>
/// <returns>SafeMemoryHandleExt object to an native (unmanaged) array of pointers</returns>
2022-01-16 19:22:34 -05:00
protected SafeMemoryHandleExt ( IntPtr [ ] values ) : this ( IntPtr . Size * values . Length ) = > CallLocked ( p = > Marshal . Copy ( values , 0 , p , values . Length ) ) ;
2017-11-27 12:18:01 -05:00
/// <summary>Allocates from unmanaged memory to represent a Unicode string (WSTR) and marshal this to a native PWSTR.</summary>
/// <param name="s">The string value.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>SafeMemoryHandleExt object to an native (unmanaged) string</returns>
2019-03-25 20:38:42 -04:00
protected SafeMemoryHandleExt ( string s , CharSet charSet = CharSet . Unicode ) : base ( IntPtr . Zero , s . GetByteCount ( true , charSet ) , true )
2017-11-27 12:18:01 -05:00
{
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
SetHandle ( StringHelper . GetCharSize ( charSet ) = = 2 ? mm . AllocStringUni ( s ) : mm . AllocStringAnsi ( s ) ) ;
}
2022-01-16 19:22:34 -05:00
/// <summary>
/// Initializes a new instance of the <see cref="SafeMemoryHandleExt{TMem}"/> class from a <see cref="SafeAllocatedMemoryHandle"/>
/// instance, copying all the memory.
/// </summary>
/// <param name="source">The source memory block.</param>
protected SafeMemoryHandleExt ( SafeAllocatedMemoryHandle source ) : base ( source ) { }
2017-11-27 12:18:01 -05:00
/// <summary>
2020-04-07 16:34:28 -04:00
/// Adds reference to other SafeMemoryHandle objects, the pointer to which are referred to by this object. This is to ensure that
/// such objects being referred to wouldn't be unreferenced until this object is active. For e.g. when this object is an array of
/// pointers to other objects
2017-11-27 12:18:01 -05:00
/// </summary>
/// <param name="children">Collection of SafeMemoryHandle objects referred to by this object.</param>
public void AddSubReference ( IEnumerable < ISafeMemoryHandle > children )
{
2022-01-16 19:22:34 -05:00
references ? ? = new List < ISafeMemoryHandle > ( ) ;
2017-11-27 12:18:01 -05:00
references . AddRange ( children ) ;
}
2020-04-07 16:34:28 -04:00
/// <summary>
/// Extracts an array of structures of <typeparamref name="T"/> containing <paramref name="count"/> items. <note type="note">This
/// call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.</note>
2017-11-27 12:18:01 -05:00
/// </summary>
/// <typeparam name="T">The type of the structures to retrieve.</typeparam>
/// <param name="count">The number of structures to retrieve.</param>
/// <param name="prefixBytes">The number of bytes to skip before reading the structures.</param>
/// <returns>An array of structures of <typeparamref name="T"/>.</returns>
public T [ ] ToArray < T > ( int count , int prefixBytes = 0 )
{
if ( IsInvalid ) return null ;
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
2019-11-21 18:33:39 -05:00
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
2022-01-16 19:22:34 -05:00
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
return CallLocked ( p = > p . ToArray < T > ( count , prefixBytes , sz ) ) ;
2017-11-27 12:18:01 -05:00
}
2020-04-07 16:34:28 -04:00
/// <summary>
/// Extracts an enumeration of structures of <typeparamref name="T"/> containing <paramref name="count"/> items. <note
/// type="note">This call can cause memory exceptions if the pointer does not have sufficient allocated memory to retrieve all the structures.</note>
2017-11-27 12:18:01 -05:00
/// </summary>
/// <typeparam name="T">The type of the structures to retrieve.</typeparam>
/// <param name="count">The number of structures to retrieve.</param>
/// <param name="prefixBytes">The number of bytes to skip before reading the structures.</param>
/// <returns>An enumeration of structures of <typeparamref name="T"/>.</returns>
public IEnumerable < T > ToEnumerable < T > ( int count , int prefixBytes = 0 )
{
2022-01-16 19:22:34 -05:00
if ( IsInvalid ) yield break ;
2017-11-27 12:18:01 -05:00
//if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes)
// throw new InsufficientMemoryException("Requested array is larger than the memory allocated.");
2019-11-21 18:33:39 -05:00
//if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
2022-01-16 19:22:34 -05:00
//Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential);
try
{
Lock ( ) ;
foreach ( var i in handle . ToIEnum < T > ( count , prefixBytes , sz ) )
yield return i ;
}
finally
{
Unlock ( ) ;
}
2017-11-27 12:18:01 -05:00
}
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <param name="len">The length.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
2019-09-27 15:32:19 -04:00
public string ToString ( int len , CharSet charSet = CharSet . Unicode ) = > ToString ( len , 0 , charSet ) ;
/// <summary>Returns a <see cref="System.String"/> that represents this instance.</summary>
/// <param name="len">The length.</param>
/// <param name="prefixBytes">Number of bytes preceding the string pointer.</param>
/// <param name="charSet">The character set of the string.</param>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public string ToString ( int len , int prefixBytes , CharSet charSet = CharSet . Unicode )
{
2022-01-16 19:22:34 -05:00
var str = CallLocked ( p = > StringHelper . GetString ( p . Offset ( prefixBytes ) , charSet , sz = = 0 ? long . MaxValue : sz - prefixBytes ) ) ;
2020-07-20 12:44:01 -04:00
return len = = - 1 ? str : str . Substring ( 0 , Math . Min ( len , str . Length ) ) ;
2019-09-27 15:32:19 -04:00
}
2017-11-27 12:18:01 -05:00
2020-04-07 16:34:28 -04:00
/// <summary>
/// Returns an enumeration of strings from memory where each string is pointed to by a preceding list of pointers of length
/// <paramref name="count"/>.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <param name="count">The count of expected strings.</param>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>Enumeration of strings.</returns>
2022-01-16 19:22:34 -05:00
public IEnumerable < string > ToStringEnum ( int count , CharSet charSet = CharSet . Auto , int prefixBytes = 0 ) = >
IsInvalid ? new string [ 0 ] : CallLocked ( p = > p . ToStringEnum ( count , charSet , prefixBytes , sz ) ) ;
2017-11-27 12:18:01 -05:00
/// <summary>
2020-04-07 16:34:28 -04:00
/// 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 two '\0' characters.
2017-11-27 12:18:01 -05:00
/// </summary>
/// <param name="charSet">The character set of the strings.</param>
/// <param name="prefixBytes">Number of bytes preceding the array of string pointers.</param>
/// <returns>An enumerated list of strings.</returns>
2022-01-16 19:22:34 -05:00
public IEnumerable < string > ToStringEnum ( CharSet charSet = CharSet . Auto , int prefixBytes = 0 ) = >
IsInvalid ? new string [ 0 ] : CallLocked ( p = > p . ToStringEnum ( charSet , prefixBytes , sz ) ) ;
2017-11-27 12:18:01 -05:00
2020-04-07 16:34:28 -04:00
/// <summary>
/// Marshals data from this block of memory to a newly allocated managed object of the type specified by a generic type parameter.
/// </summary>
2017-11-27 12:18:01 -05:00
/// <typeparam name="T">The type of the object to which the data is to be copied. This must be a structure.</typeparam>
2019-08-21 12:18:07 -04:00
/// <param name="prefixBytes">Number of bytes preceding the structure.</param>
2017-11-27 12:18:01 -05:00
/// <returns>A managed object that contains the data that this <see cref="SafeMemoryHandleExt{T}"/> holds.</returns>
2019-08-21 12:18:07 -04:00
public T ToStructure < T > ( int prefixBytes = 0 )
2017-11-27 12:18:01 -05:00
{
2018-10-26 14:24:07 -04:00
if ( IsInvalid ) return default ;
2022-01-16 19:22:34 -05:00
return CallLocked ( p = > p . ToStructure < T > ( sz , prefixBytes ) ) ;
2019-08-21 12:18:07 -04:00
}
2020-04-07 16:34:28 -04:00
/// <summary>Marshals data from a managed list of specified type to an offset within this allocated memory.</summary>
/// <typeparam name="T">
/// 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.
/// </typeparam>
2019-08-21 12:18:07 -04:00
/// <param name="items">The enumerated list of items to marshal.</param>
2020-04-07 16:34:28 -04:00
/// <param name="autoExtend">
/// if set to <c>true</c> automatically extend the allocated memory to the size required to hold <paramref name="items"/>.
/// </param>
/// <param name="offset">The number of bytes to skip before writing the first element of <paramref name="items"/>.</param>
2019-08-21 12:18:07 -04:00
public void Write < T > ( IEnumerable < T > 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 ;
}
2022-01-16 19:22:34 -05:00
CallLocked ( p = > p . Write ( items , offset , sz ) ) ;
2019-08-21 12:18:07 -04:00
}
/// <summary>Writes the specified value to an offset within this allocated memory.</summary>
/// <typeparam name="T">The type of the value to write.</typeparam>
/// <param name="value">The value to write.</param>
2020-04-07 16:34:28 -04:00
/// <param name="autoExtend">
/// if set to <c>true</c> automatically extend the allocated memory to the size required to hold <paramref name="value"/>.
/// </param>
2019-08-21 12:18:07 -04:00
/// <param name="offset">The number of bytes to offset from the beginning of this allocated memory before writing.</param>
2019-10-17 11:13:45 -04:00
public void Write < T > ( in T value , bool autoExtend = true , int offset = 0 ) where T : struct
2019-08-21 12:18:07 -04:00
{
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 ;
}
2022-01-16 19:22:34 -05:00
try { Lock ( ) ; handle . Write ( value , offset , sz ) ; }
finally { Unlock ( ) ; }
2017-11-27 12:18:01 -05:00
}
2019-11-19 14:52:44 -05:00
/// <summary>Writes the specified value to an offset within this allocated memory.</summary>
/// <param name="value">The value to write.</param>
2020-04-07 16:34:28 -04:00
/// <param name="autoExtend">
/// if set to <c>true</c> automatically extend the allocated memory to the size required to hold <paramref name="value"/>.
/// </param>
2019-11-19 14:52:44 -05:00
/// <param name="offset">The number of bytes to offset from the beginning of this allocated memory before writing.</param>
public void Write ( object value , bool autoExtend = true , int offset = 0 )
{
if ( IsInvalid ) throw new MemberAccessException ( "Safe memory pointer is not valid." ) ;
if ( value is null ) return ;
if ( autoExtend )
{
InteropExtensions . TrueType ( value . GetType ( ) , out var iSz ) ;
var reqSz = iSz + offset ;
if ( sz < reqSz )
Size = reqSz ;
}
2022-01-16 19:22:34 -05:00
CallLocked ( p = > p . Write ( value , offset , sz ) ) ;
2019-11-19 14:52:44 -05:00
}
2017-11-27 12:18:01 -05:00
/// <summary>When overridden in a derived class, executes the code required to free the handle.</summary>
/// <returns>
2020-04-07 16:34:28 -04:00
/// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it
/// generates a releaseHandleFailed MDA Managed Debugging Assistant.
2017-11-27 12:18:01 -05:00
/// </returns>
protected override bool ReleaseHandle ( )
{
var released = base . ReleaseHandle ( ) ;
handle = IntPtr . Zero ;
return released ;
}
2020-05-14 19:53:35 -04:00
#if ALLOWSPAN
/// <summary>Gets a reference to a structure based on this allocated memory.</summary>
/// <returns>A referenced structure.</returns>
public virtual ref T AsRef < T > ( ) = > ref MemoryMarshal . GetReference ( AsSpan < T > ( 1 ) ) ;
# endif
2017-11-27 12:18:01 -05:00
}
}