Welcome to Ars-Informatica  

 
 
 
 
 
 

If you want to build a ship don't herd people together to collect wood and don't assign them tasks and work, but rather teach them to long for the endless immensity of the sea. (Antoine-Marie-Roger de Saint-Exupéry)

Pinning structures in .NET


Dealing with embedded systems one of the most powerfull operations that C(C++) users are used to is the mapping of a memory area to a particular structure (struct). This simple operation that involves memory layout is lacking in .NET due to the existence of the garbage collector. To face this problem two solutions are available:

  • A dirty one: using the unsafe keyword along with the fixed keyword to pin the object in the garbage collector memory.
  • A clean one: using GCHandle and Marshal.

In this brief article this second solution is explained. Consider you have a C structure:

        
        typedef struct 
        { 
            int an_integer; 
            short a_short; 
            char a_string[16]; 
        } mystruct;
        
    

In C# (by using properties) you'd write:

        
        [StructLayout(LayoutKind.Sequential, Pack = 1)] 
        public struct MYSTRUCT 
        {
            public Int32 an_integer; 
            public Int16 a_short; 
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] 
            public string a_string; 
        }
        
    

Now to convert a byte[] (called bytes) to MYSTRUCT use the following code:

        
        GCHandle pinnedBytes = GCHandle.Alloc(bytes, GCHandleType.Pinned); 
        MYSTRUCT mystruct = (MYSTRUCT)Marshal.PtrToStructure( 
            pinnedBytes.AddrOfPinnedObject(), typeof(MYSTRUCT)
        );
        pinnedBytes.Free(); 
        
    

To convert back MYSTRUCT to byte[] write:

        
        int size = Marshal.SizeOf(mystruct); 
        byte[] bytes = new byte[size]; 
        GCHandle pinnedBytes = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        Marshal.StructureToPtr(mystruct, pinnedBytes.AddrOfPinnedObject(), true ); 
        pinnedBytes.Free();