Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

safe fixed size array in struct c#

Tags:

arrays

c#

struct

I have a C struct in an embedded MCU with about 1000 elements, and it contains lots of fixed size arrays and other structs inside, Now I want to bring the data to PC Using C#

Here is a simple preview of my struct elements in C

struct _registers
{
    char name[32];
    float calibrate[4][16];
    float DMTI;
    float DMTII;
    float DMTIII;
    float DMTIE;
    float DMTIIE;
    ....
};

Now I want to convert the Struct into C# using the GCHandle class,

something like this

//The C struct is in this byte array named buffer
byte[] buffer = new byte[4096];

        GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
       _registers stuff = (protection_registers)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(_registers));
        handle.Free();

the problem is that the Visual studio complains about the "Pointers and fixed size buffers may only be used in an unsafe context"

is there a way to use it normally without unsafe code? I have found Doing something like this

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    [FieldOffset(0)]
    public string name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(32)]
    public float calibrate[4][16];
}

but as the Code on the MCU is evolving in the coming years and we will add lots of functionality and parameter to the Struct, and since the struct has already 1000 elements, how we can do it better and more clever way? because keep tracking of all the offsets is very hard and error prone!

like image 514
ASiDesigner Avatar asked Jan 30 '23 14:01

ASiDesigner


1 Answers

Try doing something like this instead (note: using class instead of struct which is more appropriate for C# - still marshals OK to a C++ struct):

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public StringBuilder name;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4*16)]
    public float[,] calibrate;

    [MarshalAs(UnmanagedType.R4)]
    public float DMTI;

    [MarshalAs(UnmanagedType.R4)]
    public float DMTII;

    // Etc
}

You won't be able to remove the SizeConst since the marshalling needs to know this.

Also, when you intialise the class, you will need to set the array fields to the appropriate sized buffers, and initialise the StringBuilder with the correct buffer size.

Doing it this way means you can avoid using fixed buffers (and hence, you avoid the unsafe code).

like image 132
Matthew Watson Avatar answered Feb 02 '23 11:02

Matthew Watson