SilkMarshal 2.0 (#333)
* SilkMarshal 2.0 w/ GlobalMemory - a HGlobal/POH abstraction * Update src/Core/Silk.NET.Core/Native/GlobalMemory.cs * NativeStringEncoding = UnmanagedType
This commit is contained in:
Родитель
63a8826e1b
Коммит
892a931f2b
|
@ -33,7 +33,6 @@ dotnet_style_qualification_for_field = false:warning
|
|||
dotnet_style_qualification_for_method = false:warning
|
||||
dotnet_style_qualification_for_property = false:warning
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:hint
|
||||
csharp_style_expression_bodied_methods = false:hint
|
||||
csharp_style_inlined_variable_declaration = true:hint
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// You may modify and distribute Silk.NET under the terms
|
||||
// of the MIT license. See the LICENSE file for details.
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
using System.Reflection;
|
||||
using NativeLibrary3 = System.Runtime.InteropServices.NativeLibrary;
|
||||
#else
|
||||
|
@ -303,7 +303,7 @@ namespace Silk.NET.Core.Loader
|
|||
/// <returns>A LibraryLoader suitable for loading libraries.</returns>
|
||||
public static LibraryLoader GetPlatformDefaultLoader()
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
return new NetNextNativeLibraryLoader();
|
||||
#else
|
||||
|
||||
|
@ -333,7 +333,7 @@ namespace Silk.NET.Core.Loader
|
|||
throw new PlatformNotSupportedException("This platform cannot load native libraries.");
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
private class NetNextNativeLibraryLoader : LibraryLoader
|
||||
{
|
||||
protected override IntPtr CoreLoadNativeLibrary(string name)
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
// This file is part of Silk.NET.
|
||||
//
|
||||
// You may modify and distribute Silk.NET under the terms
|
||||
// of the MIT license. See the LICENSE file for details.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Silk.NET.Core.Native
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a block of global memory. That is, memory that is pinned and is valid so long as this object is alive.
|
||||
/// </summary>
|
||||
public sealed class GlobalMemory : IDisposable
|
||||
{
|
||||
// Actual object
|
||||
private readonly object _memoryObject;
|
||||
|
||||
internal GlobalMemory(object memoryObject, int length)
|
||||
{
|
||||
_memoryObject = memoryObject;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of this block of global memory.
|
||||
/// </summary>
|
||||
public int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to a specific index of this block of global memory.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
public ref byte this[int index] => ref Unsafe.Add(ref GetPinnableReference(), index);
|
||||
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
/// <summary>
|
||||
/// Gets a reference to a specific index of this block of global memory.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
public ref byte this[Index index] => ref Unsafe.Add(ref GetPinnableReference(), index.GetOffset(Length));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span representing a specific area of this block of global memory.
|
||||
/// </summary>
|
||||
/// <param name="range">The range.</param>
|
||||
public Span<byte> this[Range range]
|
||||
=> AsSpan().Slice(range.Start.GetOffset(Length), range.End.GetOffset(Length));
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to this block of global memory.
|
||||
/// </summary>
|
||||
public unsafe IntPtr Handle => (IntPtr)Unsafe.AsPointer(ref GetPinnableReference());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span representing this block of global memory.
|
||||
/// </summary>
|
||||
/// <returns>A span of global memory.</returns>
|
||||
public unsafe Span<byte> AsSpan() => _memoryObject is IGlobalMemory hGlobal
|
||||
? new Span<byte>((byte*) hGlobal.Handle, Length)
|
||||
: new Span<byte>((byte[]) _memoryObject);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span of the given type representing this block of global memory.
|
||||
/// </summary>
|
||||
/// <returns>A span of global memory.</returns>
|
||||
public unsafe Span<T> AsSpan<T>() where T : unmanaged
|
||||
=> new Span<T>(Unsafe.AsPointer(ref GetPinnableReference()), Length / sizeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span representing this block of global memory.
|
||||
/// </summary>
|
||||
/// <returns>A span of global memory.</returns>
|
||||
public static implicit operator Span<byte>(GlobalMemory left) => left.AsSpan();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to this block of global memory.
|
||||
/// </summary>
|
||||
/// <returns>A handle to this block of global memory.</returns>
|
||||
public static unsafe implicit operator void*(GlobalMemory left) => left.Handle.ToPointer();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to this block of global memory.
|
||||
/// </summary>
|
||||
/// <returns>A handle to this block of global memory.</returns>
|
||||
public static implicit operator IntPtr(GlobalMemory left) => left.Handle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the global memory. This reference is valid until this object is disposed or finalized.
|
||||
/// </summary>
|
||||
/// <returns>A reference to the global memory.</returns>
|
||||
public unsafe ref byte GetPinnableReference()
|
||||
=> ref _memoryObject is IGlobalMemory hGlobal
|
||||
? ref *(byte*) hGlobal.Handle
|
||||
: ref ((byte[]) _memoryObject)[0];
|
||||
|
||||
private void Free()
|
||||
{
|
||||
switch (_memoryObject)
|
||||
{
|
||||
case HGlobal hGlobal:
|
||||
{
|
||||
Marshal.FreeHGlobal(hGlobal.Handle);
|
||||
break;
|
||||
}
|
||||
case BStr bStr:
|
||||
{
|
||||
Marshal.FreeBSTR(bStr.Handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Free();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~GlobalMemory()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
// Allocation methods
|
||||
/// <summary>
|
||||
/// Allocates a block of global memory of the given length.
|
||||
/// </summary>
|
||||
/// <param name="length">The number of bytes to allocate.</param>
|
||||
/// <returns>A block of global memory.</returns>
|
||||
public static GlobalMemory Allocate(int length) =>
|
||||
#if !NET5_0
|
||||
new GlobalMemory(new HGlobal(length), length);
|
||||
#else
|
||||
new GlobalMemory(GC.AllocateUninitializedArray<byte>(length, true), length);
|
||||
#endif
|
||||
|
||||
// Encapsulations different kinds of memory
|
||||
private interface IGlobalMemory
|
||||
{
|
||||
IntPtr Handle { get; }
|
||||
}
|
||||
|
||||
private struct HGlobal : IGlobalMemory
|
||||
{
|
||||
public HGlobal(int length) => Handle = Marshal.AllocHGlobal(length);
|
||||
public HGlobal(IntPtr val) => Handle = val;
|
||||
public IntPtr Handle { get; }
|
||||
}
|
||||
|
||||
private struct BStr : IGlobalMemory
|
||||
{
|
||||
public BStr(int length) => Handle = SilkMarshal.AllocBStr(length);
|
||||
public BStr(IntPtr val) => Handle = val;
|
||||
public IntPtr Handle { get; }
|
||||
}
|
||||
|
||||
private struct Other : IGlobalMemory
|
||||
{
|
||||
// used for "unsafe" marshalling of a pointer to our neat GlobalMemory class if that's your thing.
|
||||
public Other(IntPtr val) => Handle = val;
|
||||
public IntPtr Handle { get; }
|
||||
}
|
||||
|
||||
// "Unsafe" methods
|
||||
internal static GlobalMemory FromHGlobal(IntPtr hGlobal, int len)
|
||||
=> new GlobalMemory(new HGlobal(hGlobal), len);
|
||||
|
||||
internal static GlobalMemory FromBStr(IntPtr bStr, int len)
|
||||
=> new GlobalMemory(new BStr(bStr), len);
|
||||
|
||||
internal static GlobalMemory FromAnyPtr(IntPtr val, int len)
|
||||
=> new GlobalMemory(new Other(val), len);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// This file is part of Silk.NET.
|
||||
//
|
||||
// You may modify and distribute Silk.NET under the terms
|
||||
// of the MIT license. See the LICENSE file for details.
|
||||
|
||||
namespace Silk.NET.Core.Native
|
||||
{
|
||||
public enum NativeStringEncoding
|
||||
{
|
||||
BStr = UnmanagedType.BStr,
|
||||
LPStr = UnmanagedType.LPStr,
|
||||
LPTStr = UnmanagedType.LPTStr,
|
||||
LPUTF8Str = UnmanagedType.LPUTF8Str,
|
||||
LPWStr = UnmanagedType.LPWStr,
|
||||
Ansi = LPStr,
|
||||
Auto = LPTStr,
|
||||
Uni = LPWStr,
|
||||
UTF8 = LPUTF8Str
|
||||
}
|
||||
}
|
|
@ -4,8 +4,10 @@
|
|||
// of the MIT license. See the LICENSE file for details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Silk.NET.Core.Native
|
||||
{
|
||||
|
@ -20,12 +22,373 @@ namespace Silk.NET.Core.Native
|
|||
/// <param name="length">The length of the string pointer, in bytes.</param>
|
||||
/// <returns>A pointer to the created string.</returns>
|
||||
public static IntPtr AllocBStr(int length) => Marshal.StringToBSTR(new string('\0', length));
|
||||
|
||||
// Store the GlobalMemory instances so that on .NET 5 the pinned object heap isn't prematurely garbage collected
|
||||
// This means that the GlobalMemory is only freed when the user calls Free.
|
||||
private static readonly ConcurrentDictionary<IntPtr, GlobalMemory> _marshalledMemory =
|
||||
new ConcurrentDictionary<IntPtr, GlobalMemory>();
|
||||
|
||||
// In addition, we should keep track of the memory we allocate dedicated to string arrays. If we don't, we won't
|
||||
// know to free the individual strings allocated within memory.
|
||||
private static readonly ConcurrentDictionary<GlobalMemory, int> _stringArrays
|
||||
= new ConcurrentDictionary<GlobalMemory, int>();
|
||||
|
||||
private static IntPtr RegisterMemory(GlobalMemory memory) => (_marshalledMemory[memory.Handle] = memory).Handle;
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a block of global memory of the given size.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The allocated memory must be manually freed using <see cref="Free"/>.
|
||||
/// </remarks>
|
||||
/// <param name="length">The number of bytes to allocate.</param>
|
||||
/// <returns>The allocated bytes.</returns>
|
||||
public static IntPtr Allocate(int length) => RegisterMemory(GlobalMemory.Allocate(length));
|
||||
|
||||
/// <summary>
|
||||
/// Frees the specific region of global memory.
|
||||
/// </summary>
|
||||
/// <param name="memory">The memory to free.</param>
|
||||
/// <returns>
|
||||
/// Whether the operation was successful or not. If false, the memory likely wasn't allocated with
|
||||
/// <see cref="Allocate" />.
|
||||
/// </returns>
|
||||
public static bool Free(IntPtr memory)
|
||||
{
|
||||
var ret = _marshalledMemory.TryRemove(memory, out var val);
|
||||
if (val is null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (_stringArrays.TryRemove(val, out var numStrings))
|
||||
{
|
||||
var span = val.AsSpan<IntPtr>();
|
||||
for (var i = 0; i < numStrings; i++)
|
||||
{
|
||||
Free(span[i]);
|
||||
}
|
||||
}
|
||||
|
||||
val.Dispose();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="GlobalMemory"/> object containing a copy of the input string marshalled per the specified
|
||||
/// native string encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to marshal.</param>
|
||||
/// <param name="encoding">The target native string encoding.</param>
|
||||
/// <returns>The <see cref="GlobalMemory"/> object containing the marshalled string array.</returns>
|
||||
public static GlobalMemory StringToMemory
|
||||
(
|
||||
string input,
|
||||
NativeStringEncoding encoding = NativeStringEncoding.Ansi
|
||||
)
|
||||
{
|
||||
return encoding switch
|
||||
{
|
||||
NativeStringEncoding.BStr => BStrToMemory(Marshal.StringToBSTR(input), input.Length),
|
||||
NativeStringEncoding.LPStr => AnsiToMemory(input),
|
||||
NativeStringEncoding.LPTStr => Utf8ToMemory(input),
|
||||
NativeStringEncoding.LPUTF8Str => Utf8ToMemory(input),
|
||||
NativeStringEncoding.LPWStr => WideToMemory(input),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(encoding))
|
||||
};
|
||||
|
||||
static unsafe GlobalMemory Utf8ToMemory(string input)
|
||||
{
|
||||
var memory = GlobalMemory.Allocate(Encoding.UTF8.GetMaxByteCount(input.Length) + 1);
|
||||
int convertedBytes;
|
||||
fixed (char* firstChar = input)
|
||||
{
|
||||
fixed (byte* bytes = memory)
|
||||
{
|
||||
convertedBytes = Encoding.UTF8.GetBytes(firstChar, input.Length, bytes, memory.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
memory[convertedBytes] = 0;
|
||||
return memory;
|
||||
}
|
||||
|
||||
static unsafe GlobalMemory AnsiToMemory(string input)
|
||||
{
|
||||
var memory = GlobalMemory.Allocate((input.Length + 1) * Marshal.SystemMaxDBCSCharSize);
|
||||
int convertedBytes;
|
||||
|
||||
fixed (char* firstChar = input)
|
||||
{
|
||||
fixed (byte* bytes = memory)
|
||||
{
|
||||
convertedBytes = Encoding.UTF8.GetBytes(firstChar, input.Length, bytes, memory.Length);
|
||||
}
|
||||
}
|
||||
|
||||
memory[convertedBytes] = 0;
|
||||
return memory;
|
||||
}
|
||||
|
||||
static unsafe GlobalMemory WideToMemory(string input)
|
||||
{
|
||||
var memory = GlobalMemory.Allocate((input.Length + 1) * 2);
|
||||
fixed (char* firstChar = input)
|
||||
{
|
||||
Buffer.MemoryCopy(firstChar, (void*) memory.Handle, memory.Length, input.Length + 1);
|
||||
}
|
||||
|
||||
return memory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to memory containing a copy of the input string marshalled per the specified
|
||||
/// native string encoding.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The allocated memory must be manually freed using <see cref="Free"/>.
|
||||
/// </remarks>
|
||||
/// <param name="input">The string to marshal.</param>
|
||||
/// <param name="encoding">The target native string encoding.</param>
|
||||
/// <returns>A pointer to the memory containing the marshalled string array.</returns>
|
||||
public static IntPtr StringToPtr(string input, NativeStringEncoding encoding = NativeStringEncoding.Ansi)
|
||||
=> RegisterMemory(StringToMemory(input, encoding));
|
||||
|
||||
/// <summary>
|
||||
/// Reads a null-terminated string from unmanaged memory, with the given native encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">A pointer to memory containing a null-terminated string.</param>
|
||||
/// <param name="encoding">The encoding of the string in memory.</param>
|
||||
/// <returns>The string read from memory.</returns>
|
||||
public static string PtrToString(IntPtr input, NativeStringEncoding encoding = NativeStringEncoding.Ansi)
|
||||
=> encoding switch
|
||||
{
|
||||
NativeStringEncoding.BStr => Marshal.PtrToStringBSTR(input),
|
||||
NativeStringEncoding.LPStr => Marshal.PtrToStringAnsi(input),
|
||||
NativeStringEncoding.LPTStr => Marshal.PtrToStringAuto(input),
|
||||
NativeStringEncoding.LPUTF8Str => Utf8PtrToString(input),
|
||||
NativeStringEncoding.LPWStr => Marshal.PtrToStringUni(input),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(encoding))
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Reads a null-terminated string from global memory, with the given native encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">Global memory containing a null-terminated string.</param>
|
||||
/// <param name="e">The encoding of the string in memory.</param>
|
||||
/// <returns>The string read from memory.</returns>
|
||||
public static string MemoryToString(GlobalMemory input, NativeStringEncoding e = NativeStringEncoding.Ansi)
|
||||
=> PtrToString(input.Handle, e);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given string array in global memory, marshalled using the specified encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array.</param>
|
||||
/// <param name="e">The encoding of the resultant string array.</param>
|
||||
/// <returns>Global memory containing the marshalled string array.</returns>
|
||||
public static GlobalMemory StringArrayToMemory
|
||||
(
|
||||
IReadOnlyList<string> input,
|
||||
NativeStringEncoding e = NativeStringEncoding.Ansi
|
||||
)
|
||||
{
|
||||
var memory = GlobalMemory.Allocate(input.Count * IntPtr.Size);
|
||||
var span = memory.AsSpan<IntPtr>();
|
||||
for (var i = 0; i < input.Count; i++)
|
||||
{
|
||||
span[i] = StringToPtr(input[i], e);
|
||||
}
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given string array in global memory, marshalled using the specified custom marshaller.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array.</param>
|
||||
/// <param name="customStringMarshaller">The custom string-to-pointer marshaller to use.</param>
|
||||
/// <returns>Global memory containing the marshalled string array.</returns>
|
||||
public static GlobalMemory StringArrayToMemory
|
||||
(
|
||||
IReadOnlyList<string> input,
|
||||
Func<string, IntPtr> customStringMarshaller
|
||||
)
|
||||
{
|
||||
var memory = GlobalMemory.Allocate(input.Count * IntPtr.Size);
|
||||
var span = memory.AsSpan<IntPtr>();
|
||||
for (var i = 0; i < input.Count; i++)
|
||||
{
|
||||
span[i] = customStringMarshaller(input[i]);
|
||||
}
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given string array in memory, marshalled using the specified encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array.</param>
|
||||
/// <param name="encoding">The encoding of the resultant string array.</param>
|
||||
/// <returns>A pointer to memory containing the marshalled string array.</returns>
|
||||
public static IntPtr StringArrayToPtr
|
||||
(
|
||||
IReadOnlyList<string> input,
|
||||
NativeStringEncoding encoding = NativeStringEncoding.Ansi
|
||||
)
|
||||
{
|
||||
var memory = StringArrayToMemory(input, encoding);
|
||||
_stringArrays.TryAdd(memory, input.Count);
|
||||
return RegisterMemory(memory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given string array in memory, marshalled using the given custom string marshaller.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array.</param>
|
||||
/// <param name="customStringMarshaller">The marshaller to use for the individual strings in the array.</param>
|
||||
/// <returns>A pointer to memory containing the marshalled string array.</returns>
|
||||
public static IntPtr StringArrayToPtr
|
||||
(
|
||||
IReadOnlyList<string> input,
|
||||
Func<string, IntPtr> customStringMarshaller
|
||||
)
|
||||
{
|
||||
var memory = StringArrayToMemory(input, customStringMarshaller);
|
||||
_stringArrays.TryAdd(memory, input.Count);
|
||||
return RegisterMemory(memory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an array null-terminated string from unmanaged memory, with the given native encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">A pointer to unmanaged memory containing a string array.</param>
|
||||
/// <param name="numStrings">The number of strings contained within the string array.</param>
|
||||
/// <param name="encoding">The encoding of the strings in memory.</param>
|
||||
/// <returns>The read string array.</returns>
|
||||
public static unsafe string[] PtrToStringArray
|
||||
(
|
||||
IntPtr input,
|
||||
int numStrings,
|
||||
NativeStringEncoding encoding = NativeStringEncoding.Ansi
|
||||
)
|
||||
{
|
||||
var ret = new string[numStrings];
|
||||
var ptrs = (IntPtr*) input;
|
||||
for (var i = 0; i < numStrings; i++)
|
||||
{
|
||||
ret[i] = PtrToString(ptrs![i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an array null-terminated string from unmanaged memory, with the given custom pointer-to-string
|
||||
/// marshaller.
|
||||
/// </summary>
|
||||
/// <param name="input">A pointer to unmanaged memory containing a string array.</param>
|
||||
/// <param name="numStrings">The number of strings contained within the string array.</param>
|
||||
/// <param name="customUnmarshaller">The pointer-to-string marshaller to use.</param>
|
||||
/// <returns>The read string array.</returns>
|
||||
public static unsafe string[] PtrToStringArray
|
||||
(
|
||||
IntPtr input,
|
||||
int numStrings,
|
||||
Func<IntPtr, string> customUnmarshaller
|
||||
)
|
||||
{
|
||||
var ret = new string[numStrings];
|
||||
var ptrs = (IntPtr*) input;
|
||||
for (var i = 0; i < numStrings; i++)
|
||||
{
|
||||
ret[i] = customUnmarshaller(ptrs![i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an array null-terminated string from global memory, with the given native encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">Global memory containing a string array.</param>
|
||||
/// <param name="encoding">The encoding of the strings in memory.</param>
|
||||
/// <returns>The read string array.</returns>
|
||||
public static string[] MemoryToStringArray
|
||||
(
|
||||
GlobalMemory input,
|
||||
NativeStringEncoding encoding = NativeStringEncoding.Ansi
|
||||
) => PtrToStringArray(input, input.Length / IntPtr.Size, encoding);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads an array null-terminated string from global memory, with the given pointer-to-string marshaller.
|
||||
/// </summary>
|
||||
/// <param name="input">Global memory containing a string array.</param>
|
||||
/// <param name="customUnmarshaller">The pointer-to-string marshaller to use.</param>
|
||||
/// <returns>The read string array.</returns>
|
||||
public static string[] MemoryToStringArray
|
||||
(
|
||||
GlobalMemory input,
|
||||
Func<IntPtr, string> customUnmarshaller
|
||||
) => PtrToStringArray(input, input.Length / IntPtr.Size, customUnmarshaller);
|
||||
|
||||
private static unsafe string Utf8PtrToString(IntPtr ptr)
|
||||
{
|
||||
#if NETCOREAPP3_1 || NET5_0
|
||||
return Marshal.PtrToStringUTF8(ptr);
|
||||
#else
|
||||
var span = new Span<byte>((void*) ptr, int.MaxValue);
|
||||
span = span.Slice(0, span.IndexOf(default(byte)));
|
||||
fixed (byte* bytes = span)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, span.Length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// "Unsafe" methods
|
||||
/// <summary>
|
||||
/// Gets a <see cref="GlobalMemory"/> object representing this HGlobal.
|
||||
/// </summary>
|
||||
/// <param name="hGlobal">The HGlobal to wrap.</param>
|
||||
/// <param name="length">The length of this HGlobal in bytes.</param>
|
||||
/// <returns>An object representing this HGlobal.</returns>
|
||||
public static GlobalMemory HGlobalToMemory(IntPtr hGlobal, int length)
|
||||
=> GlobalMemory.FromHGlobal(hGlobal, length);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="GlobalMemory"/> object representing this BStr.
|
||||
/// </summary>
|
||||
/// <param name="bStr">The BStr to wrap.</param>
|
||||
/// <param name="length">The length of this BStr in bytes.</param>
|
||||
/// <returns>An object representing this BStr.</returns>
|
||||
public static GlobalMemory BStrToMemory(IntPtr bStr, int length)
|
||||
=> GlobalMemory.FromHGlobal(bStr, length);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="GlobalMemory"/> object representing this pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr">The pointer to wrap.</param>
|
||||
/// <param name="length">The length of this pointer in bytes.</param>
|
||||
/// <returns>An object representing this pointer.</returns>
|
||||
/// <remarks>
|
||||
/// This is not recommended for use as it may be implied that freeing occurs when this object goes out of scope,
|
||||
/// even though this is not the case. If the pointer is a HGlobal or a BStr, use one of the other methods;
|
||||
/// otherwise, this method should only be used for accessing <see cref="GlobalMemory"/>'s rich set of APIs and
|
||||
/// not to manage lifetime.
|
||||
/// </remarks>
|
||||
public static GlobalMemory PtrToMemory(IntPtr ptr, int length)
|
||||
=> GlobalMemory.FromHGlobal(ptr, length);
|
||||
|
||||
// TODO !!!!!!!!!!!!!!! LEGACY METHODS START HERE, DELETE THEM ONCE SILK HAS STOPPED USING THEM !!!!!!!!!!!!!!!
|
||||
|
||||
/// <summary>
|
||||
/// Converts a C# string to an ANSI string pointer.
|
||||
/// </summary>
|
||||
/// <param name="str">The input string.</param>
|
||||
/// <returns>A pointer to a native ANSI string.</returns>
|
||||
[Obsolete]
|
||||
public static IntPtr MarshalStringToPtr(string str)
|
||||
{
|
||||
return Marshal.StringToHGlobalAnsi(str);
|
||||
|
@ -36,6 +399,7 @@ namespace Silk.NET.Core.Native
|
|||
/// </summary>
|
||||
/// <param name="str">A pointer to the ANSI string to convert.</param>
|
||||
/// <returns>A C# string.</returns>
|
||||
[Obsolete]
|
||||
public static string MarshalPtrToString(IntPtr str)
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(str);
|
||||
|
@ -45,6 +409,7 @@ namespace Silk.NET.Core.Native
|
|||
/// Free a string pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr">The pointer to free.</param>
|
||||
[Obsolete]
|
||||
public static void FreeStringPtr(IntPtr ptr)
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
|
@ -55,6 +420,7 @@ namespace Silk.NET.Core.Native
|
|||
/// </summary>
|
||||
/// <param name="length">The length of the string pointer, in bytes.</param>
|
||||
/// <returns>A pointer to the created string.</returns>
|
||||
[Obsolete]
|
||||
public static IntPtr NewStringPtr(int length)
|
||||
{
|
||||
return Marshal.AllocHGlobal(length);
|
||||
|
@ -65,6 +431,7 @@ namespace Silk.NET.Core.Native
|
|||
/// </summary>
|
||||
/// <param name="length">The length of the string pointer, in bytes.</param>
|
||||
/// <returns>A pointer to the created string.</returns>
|
||||
[Obsolete]
|
||||
public static IntPtr NewStringPtr(uint length)
|
||||
{
|
||||
return Marshal.AllocHGlobal((int) length);
|
||||
|
@ -76,6 +443,7 @@ namespace Silk.NET.Core.Native
|
|||
/// <param name="array">The array of strings to convert.</param>
|
||||
/// <returns>The new pointer.</returns>
|
||||
/// <exception cref="OutOfMemoryException">Thrown if enough memory cannot be allocated.</exception>
|
||||
[Obsolete]
|
||||
public static IntPtr MarshalStringArrayToPtr(IReadOnlyList<string> array)
|
||||
{
|
||||
var ptr = IntPtr.Zero;
|
||||
|
@ -118,6 +486,7 @@ namespace Silk.NET.Core.Native
|
|||
/// <param name="ptr">The pointer to convert.</param>
|
||||
/// <param name="length">The number of strings in the pointer.</param>
|
||||
/// <returns>An array of strings.</returns>
|
||||
[Obsolete]
|
||||
public static string[] MarshalPtrToStringArray(IntPtr ptr, int length)
|
||||
{
|
||||
var ret = new string[length];
|
||||
|
@ -131,6 +500,7 @@ namespace Silk.NET.Core.Native
|
|||
/// </summary>
|
||||
/// <param name="ptr">The pointer to convert.</param>
|
||||
/// <param name="arr">The array to fill with strings.</param>
|
||||
[Obsolete]
|
||||
public static void CopyPtrToStringArray(IntPtr ptr, string[] arr)
|
||||
{
|
||||
for (var i = 0; i < arr.Length; i++)
|
||||
|
@ -144,6 +514,7 @@ namespace Silk.NET.Core.Native
|
|||
/// </summary>
|
||||
/// <param name="ptr">The pointer to free.</param>
|
||||
/// <param name="length">The number of strings in the pointer.</param>
|
||||
[Obsolete]
|
||||
public static void FreeStringArrayPtr(IntPtr ptr, int length)
|
||||
{
|
||||
for (var i = 0; i < length; i++)
|
||||
|
|
|
@ -66,6 +66,9 @@ namespace Silk.NET.Core.Native
|
|||
|
||||
/// <summary>A platform-dependent character string: ANSI on Windows 98, and Unicode on Windows NT and Windows XP.</summary>
|
||||
LPTStr = 22,
|
||||
|
||||
/// <summary>A UTF8-encoded, null-terminated character string.</summary>
|
||||
LPUTF8Str = 48,
|
||||
|
||||
/// <summary>A platform-dependent, signed integer: 4 bytes on 32-bit, 8 bytes on 64-bit.</summary>
|
||||
/// <seealso cref="System.IntPtr" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче