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

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

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);
}

Related

Marshaling c# call to DLL library

I am having trouble calling a DLL library with a specific signature. This is on the xamarin platform and running on iOS. I am trying to get a list of DNS servers. I can call other functions without an issue, but the one I really need is not working. I am sure its becuase my struct is not correct. I believe the issue is the "union" type. I just dont know what my struct should look like to appease that parameter. Here are the relevant c++ types:
1: The DLL singature
`int res_getservers __P((res_state, union res_sockaddr_union * , int));`
2: The c++ structs
union res_sockaddr_union {
struct sockaddr_in sin;
#ifdef IN6ADDR_ANY_INIT
struct sockaddr_in6 sin6;
#endif
#ifdef ISC_ALIGN64
int64_t __align64; /* 64bit alignment */
#else
int32_t __align32; /* 32bit alignment */
#endif
char __space[128]; /* max size */
};
struct sockaddr_in6 {
__uint8_t sin6_len; /* length of this struct(sa_family_t) */
sa_family_t sin6_family; /* AF_INET6 (sa_family_t) */
in_port_t sin6_port; /* Transport layer port # (in_port_t) */
__uint32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
__uint32_t sin6_scope_id; /* scope zone index */
};
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Any help on generating the proper c# structs would be greatly appreciated! Thank you.
Disclaimer: I don't have C++ installed on my machine any more but I believe this will either work, or at least give you a good shove in the right direction.
Based on: https://msdn.microsoft.com/en-us/library/windows/desktop/ms738571(v=vs.85).aspx
And: https://learn.microsoft.com/en-us/cpp/mfc/reference/sockaddr-in-structure
And: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute(v=vs.110).aspx
And numerous other Google searches for the types in question.
using System;
using System.Runtime.InteropServices;
namespace SockStructTest
{
class Program
{
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct struct_sin_zero
{
[FieldOffset(0)]
public byte s_b1;
[FieldOffset(1)]
public byte s_b2;
[FieldOffset(2)]
public byte s_b3;
[FieldOffset(3)]
public byte s_b4;
[FieldOffset(4)]
public byte s_b5;
[FieldOffset(5)]
public byte s_b6;
[FieldOffset(6)]
public byte s_b7;
[FieldOffset(7)]
public byte s_b8;
[FieldOffset(0)]
public UInt64 s_ui64;
};
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct in_addr
{
[FieldOffset(0)]
public byte s_b1;
[FieldOffset(1)]
public byte s_b2;
[FieldOffset(2)]
public byte s_b3;
[FieldOffset(3)]
public byte s_b4;
[FieldOffset(0)]
public UInt16 s_w1;
[FieldOffset(2)]
public UInt16 s_w2;
[FieldOffset(0)]
public UInt32 S_addr;
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct sockaddr_in
{
[FieldOffset(0)]
public Int16 sin_family;
[FieldOffset(2)]
public UInt16 sin_port;
[FieldOffset(4)]
public in_addr sin_addr;
[FieldOffset(8)]
public struct_sin_zero sin_zero;
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct in6_addr_u
{
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
[FieldOffset(4)]
public byte Byte4;
[FieldOffset(5)]
public byte Byte5;
[FieldOffset(6)]
public byte Byte6;
[FieldOffset(7)]
public byte Byte7;
[FieldOffset(8)]
public byte Byte8;
[FieldOffset(9)]
public byte Byte9;
[FieldOffset(10)]
public byte ByteA;
[FieldOffset(11)]
public byte ByteB;
[FieldOffset(12)]
public byte ByteC;
[FieldOffset(13)]
public byte ByteD;
[FieldOffset(14)]
public byte ByteE;
[FieldOffset(15)]
public byte ByteF;
[FieldOffset(0)]
public UInt16 Word0;
[FieldOffset(2)]
public UInt16 Word1;
[FieldOffset(4)]
public UInt16 Word2;
[FieldOffset(6)]
public UInt16 Word3;
[FieldOffset(8)]
public UInt16 Word4;
[FieldOffset(10)]
public UInt16 Word5;
[FieldOffset(12)]
public UInt16 Word6;
[FieldOffset(14)]
public UInt16 Word7;
}
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct in6_addr
{
[FieldOffset(0)]
public in6_addr_u u;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct sockaddr_in6
{
public UInt64 sin6_len; // length of this struct(sa_family_t)
public UInt32 sin6_family; // AF_INET6 (sa_family_t)
public UInt16 sin6_port; // Transport layer port # (in_port_t)
public UInt32 sin6_flowinfo; // IP6 flow information
public in6_addr sin6_addr; // IP6 address
public UInt32 sin6_scope_id; // scope zone index
};
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct res_sockaddr_union
{
[FieldOffset(0)]
public sockaddr_in sin;
[FieldOffset(0)]
public sockaddr_in6 sin6;
[FieldOffset(0)]
public Int64 __align64;
[FieldOffset(0)]
public Int32 __align32;
// This is the char __space[128] bit.
// I've cheated and used 16 8 byte values.
[FieldOffset(0)]
public UInt64 __space0;
[FieldOffset(8)]
public UInt64 __space1;
[FieldOffset(16)]
public UInt64 __space2;
[FieldOffset(24)]
public UInt64 __space3;
[FieldOffset(32)]
public UInt64 __space4;
[FieldOffset(40)]
public UInt64 __space5;
[FieldOffset(48)]
public UInt64 __space6;
[FieldOffset(56)]
public UInt64 __space7;
[FieldOffset(64)]
public UInt64 __space8;
[FieldOffset(72)]
public UInt64 __space9;
[FieldOffset(80)]
public UInt64 __spaceA;
[FieldOffset(88)]
public UInt64 __spaceB;
[FieldOffset(96)]
public UInt64 __spaceC;
[FieldOffset(104)]
public UInt64 __spaceD;
[FieldOffset(112)]
public UInt64 __spaceE;
[FieldOffset(120)]
public UInt64 __spaceF;
}
static void Main(string[] args)
{
sockaddr_in ad;
ad.sin_zero.s_ui64 = 0;
ad.sin_family = 0; // or whatever
ad.sin_port = 1234;
// Address as b bit values
ad.sin_addr.s_b1 = 0;
ad.sin_addr.s_b2 = 1;
ad.sin_addr.s_b3 = 2;
ad.sin_addr.s_b4 = 3;
// Address as 16 bits
ad.sin_addr.s_w1 = 0;
ad.sin_addr.s_w2 = 0;
}
}
}
Best I can do - don't feel under pressure to accept the answer if it doesn't solve your issue but please do take some time to try to understand what's going on in the example I've given you.
I haven't had chance to test it - but it does build.
Good luck!
Regards,
Adam.

C# P/Invoke array of arrays

I searched this question but google gave nothing.
Is there any way to marshal array of arrays?
//C
typedef struct SomeStruct
{
float matrix[7][12];
} SomeStruct;
//C#
public struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = ???)]
public float[][] matrix;
}
You have to use a linear array in the C# code:
public struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7*12)]
public float[] matrix;
}
You'll need to convert from a 2D index to a linear index for convenience.
int LinearIndex(int i, int j)
{
return i*12 + j;
}

Translating C++ DLL into C# - How to translate a struct included in a struct

I have to translate a C++ DLL into C# to use it in a project. Here is the part that causes me trouble. This is the C++ code of the header of the DLL :
struct initiate_rb
{
unsigned char Rem_Add;
unsigned char Features_Supported_1_m;
struct add_addr_param Add_Addr_Param;
};
struct add_addr_param
{
unsigned char D_Type;
unsigned char D_Len;
struct
{
unsigned char Network_Address[6];
unsigned short MAC_Address_Len;
unsigned char * MAC_Address;
} S_Addr;
};
I am not sure how to handle this in C#. Here is what I have achieved so far :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
}
I used Pack = 1 because in my DLL header file there is the line #pragma pack (1)
The problem is, this doesn't work when I have to use this struct. It returns me an error code.
So first, concerning this struct translation, am I doing it right ?
Thanks for helping.
I'm not sure about the attributes but I believe the second struct shoudn't be nested in C# because the one in C++ defines a variable of the struct's type just after the closing parentheses.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct dpc2_add_addr_param
{
public byte D_Type;
public byte D_Len;
public S_Addr S_Addr;
}
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
In fact I resolved this problem. The problem was that I was using a constructor for my initiate_rb struct
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
public initiate_rb(int networkAddressLength) : this()
{
Rem_Add = (byte)0;
Features_Supported_1_m = (byte)0;
add_addr_param = new Add_Add_Param(networkAddressLength);
}
}
and this constructor for add_addr_param :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
public add_addr_param(int networkAddressLength) : this()
{
D_Type = (byte)0;
D_Len = (byte)0;
S_Addr.Network_Address = new byte[6]; //problem with this line
}
}
This was the problem. Since I remove this line, the code was operating correctly. In fact I didn't need constructors since all parameters have to be set to 0.
Thanks for helping!

Error: The specified structure must be blittable or have layout information

public class TestSerializer
{
public static byte[] StructureToByteArray(Test[] array)
{
int size = Marshal.SizeOf(array.Length);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(array, ptr, true);//error
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
When I write the above code I got this error. But my structure is like this :
public struct Test
{
[FieldOffset(0)]
// public string name;
public Byte icode;
[FieldOffset(1)]
public Byte method;
[FieldOffset(2)]
public Byte wav;
[FieldOffset(3)]
public Byte wav2;
[FieldOffset(4)]
public Byte unit;
[FieldOffset(5)]
public Byte temp;
[fieldOffset(6)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] itemname;
//public float factor;
}
Test[] array = new Test[200];
I want to convert this 'array' to byte array and I also have a doubt in the following line
int size = Marshal.SizeOf(array.Length);
is it possible like writing this
array[1].itemname=asd;
Try adding attribute [StructLayout(LayoutKind.Explicit)] to your struct test.
Marshal.SizeOf(array.Length) resolves to Marshal.SizeOf(200) - and 200 is an int32, for which this reports size of 4. You need to do array.Length * Marshal.SizeOf(typeof(Test))
The problem is that array of type Test is not blittable according to the documentation (while the struct itself is, if you've marked it with [StructLayout(LayoutKind.Explicit)] or LayoutKind.Sequential) and you need to do it yourself in a loop.
public static byte[] StructureToByteArray(Test[] array)
{
int structSize = Marshal.SizeOf(typeof(Test));
int size = array.Length * structSize;
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
for (int i = 0; i < array.Length; i++ )
Marshal.StructureToPtr(array[i], ptr+i*structSize, true);//error
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
Arrays must start on 4-byte boundaries, so the struct should look like this:
[StructLayout(LayoutKind.Explicit)]
public struct Test
{
[FieldOffset(0)]
public byte icode;
[FieldOffset(1)]
public byte method;
[FieldOffset(2)]
public byte wav;
[FieldOffset(3)]
public byte wav2;
[FieldOffset(4)]
public byte unit;
[FieldOffset(5)]
public byte temp;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] itemname;
}
the string assigment can be done via encoding string into byte array and copying into the struct as follows:
Test[] array = new Test[200];
byte[] temp = Encoding.UTF8.GetBytes("asd");
array[0].itemname = new byte[20];
Array.Copy(temp, array[0].itemname, temp.Length);

Casting explicitly-laid out structures

Let's say I have this structure,
[StructLayout(LayoutKind.Explicit)]
public struct Chapter4Time
{
[FieldOffset(0)]
public UInt16 Unused;
[FieldOffset(2)]
public UInt16 TimeHigh;
[FieldOffset(4)]
public UInt16 TimeLow;
[FieldOffset(6)]
public UInt16 MicroSeconds;
}
and this structure.
[StructLayout(LayoutKind.Explicit)]
public struct IEEE_1588Time
{
[FieldOffset(0)]
public UInt32 NanoSeconds;
[FieldOffset(4)]
public UInt32 Seconds;
}
How would I convert from one structure to the other?
Two options here:
No unsafe code, but explicit structure layout
(Note that although this isn't unsafe as far as the C# compiler is concerned, some frameworks may still disallow it - see Marc Gravell's comment.)
You could use a union type, which is just another struct with two fields, both set explicitly to the same location. Here's a complete example using your structures:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct Chapter4Time
{
[FieldOffset(0)]
public UInt16 Unused;
[FieldOffset(2)]
public UInt16 TimeHigh;
[FieldOffset(4)]
public UInt16 TimeLow;
[FieldOffset(6)]
public UInt16 MicroSeconds;
}
[StructLayout(LayoutKind.Explicit)]
public struct IEEE_1588Time
{
[FieldOffset(0)]
public UInt32 NanoSeconds;
[FieldOffset(4)]
public UInt32 Seconds;
}
[StructLayout(LayoutKind.Explicit)]
public struct TimeUnion
{
[FieldOffset(0)]
public Chapter4Time Chapter4Time;
[FieldOffset(0)]
public IEEE_1588Time IEEE_1588Time;
}
class Test
{
static void Main()
{
var ch4 = new Chapter4Time { TimeLow = 100, MicroSeconds = 50 };
var union = new TimeUnion { Chapter4Time = ch4 };
Console.WriteLine(union.IEEE_1588Time.Seconds);
}
}
Unsafe code, casting pointers
An alternative to the union type if you can use unsafe code is to cast a pointer of type Chapter4Time* to IEEE_1588Time*:
class Test
{
unsafe static void Main()
{
var ch4 = new Chapter4Time { TimeLow = 100, MicroSeconds = 50 };
var ieee1588 = *((IEEE_1588Time*) &ch4);
Console.WriteLine(ieee1588.Seconds);
}
}
Personally I'd avoid doing any of this if at all possible, but if you *really, really want to do it, these are probably the simplest approaches.

Categories

Resources