5

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!

sehe
  • 374,641
  • 47
  • 450
  • 633
ASiDesigner
  • 115
  • 1
  • 1
  • 8
  • tag spamming is bad netiquette – sehe Sep 18 '17 at 12:46
  • You don't need to specify `FieldOffset` as long as you define the sizes of all the fields correctly (which you are doing). – Matthew Watson Sep 18 '17 at 12:48
  • You should not declare `_registers` as a `struct` if it has 1000 elements. Declare it as a `class` instead. Otherwise each assignment will create a new `_registers` copy. The difference between `class` and `struct` in C++ is not the same as the difference between `class` and `struct` in C#. – Martin Liversage Sep 18 '17 at 12:54
  • Is there a way to remove the SizeConst too? – ASiDesigner Sep 18 '17 at 12:56
  • thanks, would you give me a sniper code? – ASiDesigner Sep 18 '17 at 12:56
  • XY problem. 1) such a giant struct on an MCU is bad design already. 2) using n´machine-dependent datatypes. 3) use a **well-defined** exchange format, not just cast the structure to an octetstream. – too honest for this site Sep 18 '17 at 13:03
  • @ASiDesigner: We are neither a coding nor a consulting site! As I wrote, your approach is broken by design. Looks like someone hacked this together without even thinking any bit (literally) further. – too honest for this site Sep 18 '17 at 13:10
  • You are asking for a solution to a problem you do not want to solve. This is going to scale very poorly no matter what you do. But at least consider using C++/CLI so the native structure can be understood by the compiler. You'll still have to shovel the data across the divide but at least the C# code will be resilient to changes in the MCU code. – Hans Passant Sep 18 '17 at 13:18

1 Answers1

11

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).

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 1
    [FieldOffset] is not unsafe. His code snippet is lousy, it is missing the `fixed` keyword. Required to make it compile. Fixed-size buffers are unsafe, indexing them is not checked. – Hans Passant Sep 18 '17 at 13:25
  • @HansPassant Aye, I removed my comments about FieldOffset. I'd misremembered an old discussion where [someone proposed that FieldOffset should be unsafe](https://stackoverflow.com/questions/4519574/should-changing-the-contents-of-a-string-like-this-cause-an-exception) - but of course, that never happened... – Matthew Watson Sep 18 '17 at 13:41
  • @HansPassant `[FieldOffset]` can be used to break .NET's type safety, at least amongst reference-types and so allows for type-punning (and not particularly useful type-puns either...), but how is that "not unsafe"? – Dai Nov 26 '21 at 02:09