2222#endregion
2323
2424using System . ComponentModel ;
25+ using System . Diagnostics . CodeAnalysis ;
2526using System . Runtime . InteropServices ;
2627
2728namespace SDL3 ;
@@ -55,7 +56,7 @@ public static partial class SDL
5556 /// <seealso cref="PointerToStringArray(nint)"/>
5657 /// <seealso cref="PointerToStringArray(nint, int)"/>
5758 /// <seealso cref="PointerToStructureArray{T}"/>
58- public static T ? PointerToStructure < T > ( IntPtr pointer ) where T : struct
59+ public static T ? PointerToStructure < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ] T > ( IntPtr pointer ) where T : struct
5960 {
6061 return pointer == IntPtr . Zero ? null : Marshal . PtrToStructure < T > ( pointer ) ;
6162 }
@@ -145,7 +146,7 @@ public static IntPtr StructureToPointer<T>(T? structure) where T : struct
145146 /// <seealso cref="PointerToStructureArray{T}"/>
146147 public static IntPtr StructureArrayToPointer < T > ( T [ ] array ) where T : struct
147148 {
148- if ( array == null || array . Length == 0 ) return IntPtr . Zero ;
149+ if ( array . Length == 0 ) return IntPtr . Zero ;
149150
150151 var sizeOfT = Marshal . SizeOf < T > ( ) ;
151152 var unmanagedPointer = Marshal . AllocHGlobal ( sizeOfT * array . Length ) ;
@@ -351,6 +352,15 @@ public static IntPtr StringToPointer(string? str)
351352 Marshal . Copy ( utf8Bytes , 0 , unmanagedPointer , utf8Bytes . Length ) ;
352353 return unmanagedPointer ;
353354 }
355+
356+
357+ /// <summary>
358+ /// Converts a unmanaged pointer to an UTF-8 string
359+ /// </summary>
360+ public static string ? PointerToString ( IntPtr pointer )
361+ {
362+ return Marshal . PtrToStringUTF8 ( pointer ) ;
363+ }
354364
355365
356366 /// <summary>
@@ -387,7 +397,7 @@ public static IntPtr StringToPointer(string? str)
387397 /// <seealso cref="PointerToPointerArray"/>
388398 /// <seealso cref="PointerToStringArray(nint)"/>
389399 /// <seealso cref="PointerToStringArray(nint, int)"/>
390- public static unsafe T [ ] ? PointerToStructureArray < T > ( IntPtr pointer , int count ) where T : struct
400+ public static unsafe T [ ] ? PointerToStructureArray < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ] T > ( IntPtr pointer , int count ) where T : struct
391401 {
392402 if ( pointer == IntPtr . Zero || count < 0 ) return null ;
393403
@@ -405,14 +415,61 @@ public static IntPtr StringToPointer(string? str)
405415 for ( var i = 0 ; i < count ; i ++ )
406416 {
407417 var elementPtr = Marshal . ReadIntPtr ( pointer , i * sizePtr ) ;
408- array [ i ] = Marshal . PtrToStructure < T > ( elementPtr ) ! ;
418+ array [ i ] = Marshal . PtrToStructure < T > ( elementPtr ) ;
409419 }
410420 }
411421
412422 return array ;
413423 }
414424
415425
426+ /// <summary>
427+ /// Allocates unmanaged memory for an array of UTF-8 string pointers (char*).
428+ /// Each string is converted via <see cref="StringToPointer(string?)"/>.
429+ /// — The array is terminated with a trailing <see cref="IntPtr.Zero"/>.
430+ /// </summary>
431+ /// <param name="array">
432+ /// — The managed array of strings. Can be <c>null</c> or empty.
433+ /// </param>
434+ /// <returns>
435+ /// — A pointer to unmanaged memory containing an array of <see cref="IntPtr"/> pointers,
436+ /// — each pointing to a UTF-8 null-terminated string.
437+ /// — Returns <see cref="IntPtr.Zero"/> for <c>null</c> or empty arrays.
438+ /// </returns>
439+ /// <remarks>
440+ /// The caller is responsible for freeing all allocated memory:
441+ /// 1. Free each individual string pointer using <see cref="Marshal.FreeHGlobal"/>.
442+ /// 2. Free the returned array pointer using <see cref="Marshal.FreeHGlobal"/>.
443+ /// Failure to do so will cause memory leaks.
444+ /// </remarks>
445+ public static IntPtr StringArrayToPointer ( string [ ] ? array )
446+ {
447+ unsafe
448+ {
449+ if ( array == null || array . Length == 0 )
450+ return IntPtr . Zero ;
451+
452+ var total = array . Length + 1 ;
453+ var size = IntPtr . Size * total ;
454+
455+ var blockPtr = Marshal . AllocHGlobal ( size ) ;
456+
457+ var zeroInit = new Span < byte > ( ( void * ) blockPtr , size ) ;
458+ zeroInit . Clear ( ) ;
459+
460+ for ( var i = 0 ; i < array . Length ; i ++ )
461+ {
462+ var strPtr = StringToPointer ( array [ i ] ) ;
463+ Marshal . WriteIntPtr ( blockPtr , i * IntPtr . Size , strPtr ) ;
464+ }
465+
466+ Marshal . WriteIntPtr ( blockPtr , array . Length * IntPtr . Size , IntPtr . Zero ) ;
467+
468+ return blockPtr ;
469+ }
470+ }
471+
472+
416473 /// <summary>
417474 /// Indicates that a method is a <c>#define</c> macro.
418475 /// </summary>
0 commit comments