4

I have to Develop a Service (C#) which read data from Network Device via TCP Socket and convert this is C# structure.

I am basing on existing, old Delphi application which is doing all this stuff and I have to migrate logic in C#.

EDITED: I got a snapshot from C-Source of original data-structure:

struct _RequestMsgStruct
{
    UCHAR           request_ver; //In DELPHI it is represented as Byte
    USHORT          mac_addr[3];    /* MAC Address */
    UINT            product_type; //In DELPHI - Cardinal
    UCHAR           supply_type; //In DELPHI - Byte
    short           reserved0; //In DELPHI - SmallInt
    UCHAR           oper_ver[4]; //In DELPHI - CARDINAL !!!
    USHORT          brd_id; //In DELPHI - WORD
    unsigned short  exp_id1; //In DELPHI - WORD

    //In DELPHI - string[15]; //Array [0..15] of char;
    UCHAR           serial_no[16]; /* Serial Number. 16th char have to be NULL */ 
    UCHAR           _name[32]; /* Name */ //Length of payload may vary //In DELPHI - string[31]

    float           data_avg; //In DELPHI - Single
    ULONG           key[5]; //In DELPHI - array [0..19] of Byte
}__attribute__ ((packed));

There is Delphi Packed record with over 200 fields of different types... it look approximately like:

  TREC_DATA = packed record
      ID            : Byte;
      MAC_ADDRESS   : array [0..5] of Byte;
      fieldCard     : cardinal;
      fieldSI       : SmallInt; 
      fieldW        : WORD;
      SERIAL_NUMBER : string[15]; //Array [0..15] of char;
      fieldSingle   : Single;
      fieldArrOfB   : array [0..19] of Byte;
    end;

To move byte array to structure in Delphi there is next code:

Move(inBytesArr[StartIdx], DelphiStruct, aMsgSize)

To convert string files (e.g. SERIAL_NUMBER) there is also such code:

var
  pc: Pchar;
...
pc := @inBytesArr[StartIdx + SerialN_Pos_Idx];
DelphiStruct.SERIAL_NUMBER := pc;

I having deal with such conversion for first time and I don't know from where to start:

  • How to convert this structure to c#? -- Should I use LayoutKind.Sequential or LayoutKind.Explicit, with or wiyhout [FieldOffset(N)] attribute? -- How I have to declare array of bytes in target c# structure: as fixed buffer or using [MarshalAs(UnmanagedType.ByValArray...)] attribute?

  • Which is better way to marshal input bytes array to final C# structure: using Marshal.PtrToStructure or GCHandle.Alloc(bytes, GCHandleType.Pinned) + AddrOfPinnedObject?

Please help me, at least, to get start point in understating from where i need to start.

5
  • 1
    I think you've given us the wrong structure declaration. You need to give us the one that matches what is on the wire. I guess that's in the C program. You are confusing us with these references to Delphi string[] types. Commented Sep 28, 2012 at 8:51
  • 1
    @DavidHeffernan, I got a snapshot of original C-Structure. Updated in question. Commented Sep 28, 2012 at 14:52
  • 2
    You can accept Dennis's answer Commented Sep 28, 2012 at 15:11
  • 1
    By the way.. Dennis's solution is working! Thanks Commented Oct 4, 2012 at 23:06
  • 1
    Yes. Why have you not accepted his answer? Commented Oct 5, 2012 at 6:16

1 Answer 1

4

By default, Delphi's packed records align fields by single byte boundary.
Hence, you should use something like this:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TREC_DATA
{
      public byte ID;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
      public byte[] MAC_ADDRESS;
      public uint fieldCard;
      public short fieldSI;
      public ushort fieldW;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
      public byte[] SERIAL_NUMBER;
      public float fieldSingle;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
      public byte[] fieldArrOfB;    
} 

The only thing I'm not sure (and can't test now without Deplhi), is a SERIAL_NUMBER field.

After your update: in original, SERIAL_NUMBER is just a null-terminated string.

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

10 Comments

There's no way to get pinvoke marshaller to deal with string[15]
@DavidHeffernan, Dennis, thanks for replies - I will check them. I want to specify problem under hood: Incoming data are bytes array from network device (via TCP Socket), device firmware is embedded C under UNIX and I don't have respective sources. Whta I have is big old Delphi application and I vahe to move/migrate device-communication to new C# Service, for this reason I am looking at the actual Delphi code, where I have some simple memory copying: Move(inBytesArr[inIdx], DelphiStruct, aMsgSize). I am supposing that for Delphi string in incoming data already exists 0-prefix byte.
@ALZ: this looks very strange... I don't believe, that some C-written software builds data packet for Delphi app only. Maybe, there's something like this (in C code): byte stringLength; byte[15] serialNumber;? Anyway, I'm inclining, that David is right, and you should marshal this member as a byte array.
Why did you wait until now to tell us that?! Your question looked like p/invoke! Please edit the question to include this info.
@ALZ: looks like usual C null-terminated string, that later being converted to Delphi short string.
|

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.