4

I'm writing a program that will communicate with a C++ program via UDP. The other program is already written (not by me). I have gotten a .h file that defines two struct that is used for the data.

EDIT: This means that I can't change the data format. I need to be able to read and write according to the C++ .h file structs!

How would I do this in C#? I will send and receive data in this format.

struct mdata
{
  uint32_t  mark_kupnr;
  uint16_t  mark_provnr;
  uint16_t  markriktning;
  uint16_t  xpos;
  uint16_t  ypos;
};

typedef struct  
{
  uint32_t      kupnr;
  uint16_t      lngd;
  uint16_t      bredd;
  uint16_t      tjocklek;
  char          slagkraft;
  uint8_t       antal;
  struct mdata  mark[10];
} markdata;

EDIT: I have tried to create the corresponding structs in C# but it's not working

BTW, I'm running on Windows and the C++ program is running on Linux. In the "specification" it says that the data should be Little Endian.

struct mdata
{
  UInt32    mark_kupnr;
  UInt16    mark_provnr;
  UInt16    markriktning;
  UInt16    xpos;
  UInt16    ypos;
};

typedef struct  
{
  UInt32        kupnr;
  UInt16        lngd;
  UInt16        bredd;
  UInt16        tjocklek;
  char          slagkraft;
  byte          antal;
  // here I have some trouble
  mdata[]   mark[10]; //???
} markdata;
7
  • 1
    C++ char is C# sbyte. Commented Nov 17, 2015 at 21:58
  • 1
    The OP should also check the settings in effect for struct packing in C++, and if necessary match it in C# using [StructLayout(Pack=<whatever>)]. Commented Nov 17, 2015 at 22:09
  • 1
    @dxiv: While that's commonly a concern, none of the structures in this question will require padding, regardless of the packing setting. The elements are already naturally aligned. Commented Nov 17, 2015 at 22:16
  • @BenVoigt that's true in this case, still good to keep in mind if there may be other structures to be converted besides those posted. Commented Nov 17, 2015 at 22:55
  • I have updated my answer. Commented Nov 19, 2015 at 14:02

3 Answers 3

2

It sounds as if you're looking for the fixed keyword. Also char in C# is twice the size as char in C++, so you need to use the correct corresponding type, sbyte.

Here's the way your second structure should be defined:

struct markdata
{
  UInt32        kupnr;
  UInt16        lngd;
  UInt16        bredd;
  UInt16        tjocklek;
  sbyte         slagkraft;
  byte          antal;
  fixed   mdata mark[10];
}

If you get wrong values, check whether they are byte swapped. But based on the systems in use and data format specification, endianness will most likely be correct by default.

Sign up to request clarification or add additional context in comments.

1 Comment

Unfortunately I get this error: Error 1 Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double.
1

You can avoid all the error-prone and low-level issues of data alignment if you serialize your structures to a platform-agnostic format.

Consider generating serialization/deserialization code automatically using Google Protocol Buffers or the Apache Thrift. These work neatly across multiple languages and data types, are widely used (tested and debugged). Each of these libraries requires you to keep a primary blueprint of your struct in a format somewhat similar to Microsoft IDL. Then you can generate interop code for any language easily.

Personally with both of these libraries I've had a problem with the reliance of the generated C++ code on STL (although for most projects this wouldn't be an issue), so I chose instead to serialize data to JSON format and send/receive the JSON as 0-terminated string over the wire. I used the tiny, header-only RapidJSON C++ library and implemented both serialization & deserialization code manually with RapidJSON.

2 Comments

As was said above we must connect to existing data format so JSON and XML is not an answer.
You are probably right, but there is a small chance that the "other program" can / will be changed. But yeah, looks like I've answered a different question.
0

If your C++ program host is IBM-PC compatible machine you must perform byte swap on your C# client. If is not than you must not.

To communicate with C# programs you must reverse data you read wich is bigger than 1 byte, because C# use big-endian byte order instead of little-endian of native C++. To swap WORD(16 bits) and DWORD(32 bits) you may use this macro

// BYTE SWAPING IN WORD and DWORD

#define WORD_SWAP(x)    ((((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00))

#define DWORD_SWAP(x)   ((((x) >> 24) & 0x000000ff) | \
                         (((x) >> 8) & 0x0000ff00) | \
                         (((x) << 8) & 0x00ff0000) | \
                         (((x) << 24) & 0xff000000))

or similar function in C#. Apply only to separate stucture fields.

PS: Do not forget compiler packing features to correct data meaning on both sides.

For more info visit Endianness, Data structure alignment

So people ask me to do the same in C#

// Swaps uint16_t
ushort Swap(ushort x)
{
     return (ushort)((((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00));
}

// Swaps uint32_t
uint Swap(uint x)
{
     return ((((x) >> 24) & 0x000000ff) |
             (((x) >> 8) & 0x0000ff00) |
             (((x) << 8) & 0x00ff0000) |
             (((x) << 24) & 0xff000000));
}

work with C# structures client means UDPClient object. It is all must be performed in low level manner.

struct mdata
{
  UInt32  mark_kupnr;
  UInt16  mark_provnr;
  UInt16  markriktning;
  UInt16  xpos;
  UInt16  ypos;
}

// example of mdata read with sockets
mbata data;
var bytes = client.Receive()

data.mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
data.mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
data.markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
data.xpos = Swap(BitConverter.ToUInt16(bytes, 8));
data.ypos = Swap(BitConverter.ToUInt16(bytes, 10));

Form output data you must do reverse from UInt32 and UInt16 to byte array. Than merge all of this in proper order using List<byte>.AddRange(variable_byte_array); than cast this formed list to array of bytes and use UDPClient.Send(formed_array);.

Array in C++ places in memory at this order [0][1][2][3][4][5][6][7][8][9]. So you must sequentualy read mdata structure 10 times form 0 to 9.

// read first data
typedef struct  
{
  UInt32        kupnr;
  UInt16        lngd;
  UInt16        bredd;
  UInt16        tjocklek;
  sbyte          slagkraft;
  byte          antal;
  // now read mkdata 10 times
  struct mdata  mark[10];
} markdata; // we have done with markdata

No one holds you to use classes instead of structures. It will be simpler to deal with arrays considering we use byte (binary) serialization at all. Than add to each class proper method (for example)

mdata class or structure methods

void Receive(UDPClient client)
{
    var bytes = client.Receive()

    mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
    mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
    markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
    xpos = Swap(BitConverter.ToUInt16(bytes, 8));
    ypos = Swap(BitConverter.ToUInt16(bytes, 10));
}

void Send(UDPClient client)
{
    var listOfBytes = new List<byte>();

    listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_kupnr)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_provnr)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(markriktning)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(xpos)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(ypos)));

    client.Send(listOfBytes.ToArray(), listOfBytes.Count);
}

void Receive(byte[] bytes)
{      
    mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
    mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
    markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
    xpos = Swap(BitConverter.ToUInt16(bytes, 8));
    ypos = Swap(BitConverter.ToUInt16(bytes, 10));
}

void Send(out byte[] bytes)
{
    var listOfBytes = new List<byte>();

    listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_kupnr)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_provnr)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(markriktning)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(xpos)));
    listOfBytes.AddRange(BitConverter.GetBytes(Swap(ypos)));

    bytes = listOfBytes.ToArray();
}

array in your markdata class not suppoused to be fixed so you just create that number of elements in your class that you want. All dirty work will go to the send and receive methods.

to deal with arrays you can add byte shift value to step through mdata array.

12 Comments

little-endian of native C++ C++ has no endianess, it relies only on the platform below (CPU...). Depending on the rest, it can be any endianess.
The C++ side is already defined for the OP. He needs to deal with the C# side, so your answer is kind of irrelevant
@Peter M: Swaping bytes works in both side mean S(S(M)) = M
@Peter M: C# works exactly in the same manner exept usage function instread of c++ macro.
And you have presented a C++ solution to swapping bytes, when the OP is looking for C# solutions. and that what he is looking for is more than just byte swapping. and when it comes to it, you should be using standard macros rather than rolling your own
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.