How to marshal an array of fixed size structs from unmanaged to managed memory

Magne 126 Reputation points
2023-01-29T21:43:46.12+00:00

.Net 7

Having an IntPtr (nint) pointing to an array of fixed size and blittable structs in unmanaged memory.

Marshal.Copy does not seem to have any method like:
Copy(myPointer, myStrcuctArray, 0, myStructArray.Length)
(It accepts an array of int, byte etc., but not an array of MyStruct)
but I do not understand why not.

After all the struct has fixed size (no strings or other classes as members).

There "must" be a Marshal method to do this kind of marshalling without lots of boiler code to create the structs from bytes in memory pointed to?

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,218 questions
.NET Runtime
.NET Runtime
.NET: Microsoft Technologies based on the .NET software framework.Runtime: An environment required to run apps that aren't compiled to machine language.
1,118 questions
0 comments No comments
{count} votes

Accepted answer
  1. Reza Aghaei 4,936 Reputation points MVP
    2023-01-29T21:57:50.3466667+00:00

    You should be able to use the pointer to get the elements one by one. Take a look at this post: Getting Array of struct from IntPtr.

    I also managed to do it using RtlMoveMemory, and Buffer.CopyMemory.

    Example 1 - Using RrlMoveMemory

    I also managed to do it using RtlMoveMemory:

    using System.Runtime.InteropServices;
    
    namespace SampleConsoleApp
    {
        internal static class Program
        {
            [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", ExactSpelling = true)]
            public static extern void RtlMoveMemory(IntPtr dest, IntPtr src, int count);
    
            [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int x, y;
            }
            [STAThread]
            static void Main()
            {
                var srcPtr = GetPOINTs();
                var destArray = new POINT[3];
                var size = Marshal.SizeOf(typeof(POINT)) * destArray.Length;
                var destPtr = Marshal.UnsafeAddrOfPinnedArrayElement(destArray, 0);
                RtlMoveMemory(destPtr, srcPtr, size);
    
                foreach (var point in destArray)
                    Console.WriteLine($"x:{point.x}, y:{point.y}");
    
                Console.ReadLine();
            }
            static IntPtr GetPOINTs()
            {
                var array = new[] {
                     new POINT { x = 1, y = 1 },
                     new POINT { x = 2, y = 2 },
                     new POINT { x = 3, y = 3 },
                };
                return Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
            }
        }
    }
    

    Example 2 - Using Buffer.CopyMemory

    I also managed to do it using Buffer.CopyMemory which requires unsafe context:

    using System.Runtime.InteropServices;
    
    namespace SampleConsoleApp
    {
        internal static class Program
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int x, y;
            }
            [STAThread]
            static unsafe void Main()
            {
                var srcPtr = GetPOINTs();
                var destArray = new POINT[3];
                var size = Marshal.SizeOf(typeof(POINT)) * destArray.Length;
                var destPtr = Marshal.UnsafeAddrOfPinnedArrayElement(destArray, 0);
                Buffer.MemoryCopy(srcPtr.ToPointer(), destPtr.ToPointer(), size, size);
                foreach (var point in destArray)
                    Console.WriteLine($"x:{point.x}, y:{point.y}");
    
                Console.ReadLine();
            }
            static IntPtr GetPOINTs()
            {
                var array = new[] {
                     new POINT { x = 1, y = 1 },
                     new POINT { x = 2, y = 2 },
                     new POINT { x = 3, y = 3 },
                };
                return Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
            }
        }
    }
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful