Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I'd like to use union as C# with byte array

Tags:

c#

union

packets

I had idea about message parsing in serial communication, there are many kind of packets which have different form. but they're all sent by byte array.

so I thought using union to parse each message. but it's not working well. following code is the sample code which I'm In Error

[StructLayout(LayoutKind.Explicit, Size=12)]
public struct UnionPacket
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=12)]
    public byte[] data;

    [FieldOffset(0)]
    public float Time;
    [FieldOffset(4)]
    public Int16 CoordX;
    [FieldOffset(6)]
    public Int16 CoordY;
    [FieldOffset(8)]
    public byte Red;
    [FieldOffset(9)]
    public byte Green;
    [FieldOffset(10)]
    public byte Blue;
    [FieldOffset(11)]
    public byte Alpha;
}

if this were possible, it would be very happy, but it isn't. This code occurs TypeLoadException "... because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field."

So I changed some code like this

[StructLayout(LayoutKind.Explicit, Size= 12)]
public struct UnionPacket
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    [FieldOffset(0)]
    public byte[] data;

    //[FieldOffset(0)]
    //public float Time;
    [FieldOffset(4)]
    public Int16 CoordX;
    [FieldOffset(6)]
    public Int16 CoordY;
    [FieldOffset(8)]
    public byte Red;
    [FieldOffset(9)]
    public byte Green;
    [FieldOffset(10)]
    public byte Blue;
    [FieldOffset(11)]
    public byte Alpha;
}

for test, i just disabled Time field which offset is 0, and this didn't occur Exception. but, if i change other fields, it doesn't change byte array. i think that byte array's real memory location is allocated in other heap, so it cannot be done.

Is there any way to solve this problem in C#? only C++ or C can solve this? and if i use this with inheritance, is it possible?

P.S. Sorry about my poor english

like image 273
JaeSang Yoo Avatar asked Feb 18 '23 18:02

JaeSang Yoo


1 Answers

I'm not 100% sure what you are trying to do, but:

first approach, with array stored inside of a structure is possible, if you use it with fixed/unsafe keywords. I don't know if this is possible for you. So having code like this should work:

[StructLayout(LayoutKind.Explicit, Size = 12)]
public unsafe struct UnionPacket
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
    [FieldOffset(0)]
    public fixed byte data[12];

    [FieldOffset(0)]
    public float Time;
    [FieldOffset(4)]
    public Int16 CoordX;
    [FieldOffset(6)]
    public Int16 CoordY;
    [FieldOffset(8)]
    public byte Red;
    [FieldOffset(9)]
    public byte Green;
    [FieldOffset(10)]
    public byte Blue;
    [FieldOffset(11)]
    public byte Alpha;
}

Of course, then you have to enable unsafe code blocks in VS and use wrap your structure around with it:

unsafe
{
    var u = new UnionPacket();
    for (byte i = 0; i < 12; i++)
    {
         u.data[i] = i;
    }
    Console.WriteLine(u.Time);
    Console.WriteLine(u.CoordX);
    Console.WriteLine(u.CoordY);        
    Console.WriteLine(u.Red);
    Console.WriteLine(u.Green);
    Console.WriteLine(u.Blue);
    Console.WriteLine(u.Alpha);
}

Other approach would be - just forgetting about this array inside struct, and handle parsing using Marshal.Copy:

[StructLayout(LayoutKind.Explicit, Size = 12)]
public struct UnionPacket2
{
    [FieldOffset(0)]
    public float Time;
    [FieldOffset(4)]
    public Int16 CoordX;
    [FieldOffset(6)]
    public Int16 CoordY;
    [FieldOffset(8)]
    public byte Red;
    [FieldOffset(9)]
    public byte Green;
    [FieldOffset(10)]
    public byte Blue;
    [FieldOffset(11)]
    public byte Alpha;
}

static void Main(string[] args)
{
    var len = Marshal.SizeOf(typeof(UnionPacket2));
    var buffer = new byte[len];
    for (byte i = 0; i < len; i++)
    {
        buffer[i] = i;
    }

    var ptr = Marshal.AllocHGlobal(len);
    Marshal.Copy(buffer, 0, ptr, len);
    var u = (UnionPacket2)Marshal.PtrToStructure(ptr, typeof(UnionPacket2));
    Marshal.FreeHGlobal(ptr);
    Console.WriteLine(u.Time);
    Console.WriteLine(u.CoordX);
    Console.WriteLine(u.CoordY);
    Console.WriteLine(u.Red);
    Console.WriteLine(u.Green);
    Console.WriteLine(u.Blue);
    Console.WriteLine(u.Alpha);
}
like image 70
Marek Kembrowski Avatar answered Feb 28 '23 10:02

Marek Kembrowski