Home > Software engineering >  Problem sending structure containing double variables over network from C# to C
Problem sending structure containing double variables over network from C# to C

Time:06-21

in a previous post I asked "how to send a Struct from C# (Win10) to C (Unix-like OS) via socket".

The suggested solution was Marshalling.

Now I have no problem sending Integers, Enums or Strings. Unfortunately I have problems sending or receiving (I don't know if I'm wrong the Send of C # or the Read of C) the Double and Long data.

C Structure declared in Unix-like OS (vxWorks):

enum TYPE_OP 
{
  START_INITIALIZE   = 0,
  STOP_INITIALIZE    = 1,
  IDENTIFY           = 2,        
  GET_VALUE          = 3,
  SET_VALUE          = 4,
  SET_RSE_MODE       = 5,
  GET_VALUE_DOUBLE   = 6,
  SET_VALUE_DOUBLE   = 7,
  SET_STRING         = 8,
  GET_VALUE_FLOAT    = 9,
  SET_VALUE_FLOAT    = 10,
  SET_TARGET         = 11,
  GET_TARGET         = 12,
  GET_TASK_STATUS    = 13
}; 

typedef struct 
{  
   long    addr;    //address of an element in GPIB chain (ex. 1, 13, etc.)
   long    value;   //number (ex. enumerative position)
   double  dvalue;  //a double number
} DESCR_DATA;


struct VAR_DATA   
{  
   int     len ;
   enum TYPE_OP type;   
   char         name[100]; //Name of the element in GPIB chain
   DESCR_DATA   data;
}; 

then after the "accept" in C:

bytes_read = read(client_sock, &vardata, sizeof(vardata));
printf("ENUM VALUE = %d\n", vardata.type);
printf("DOUBLE VALUE = %.2f\n", vardata.data.value);

but result is always 0.00

C# structure declared in Windows:

public enum TYPE_OP
{
    START_INITIALIZE = 0,
    STOP_INITIALIZE = 1,
    IDENTIFY = 2,
    GET_VALUE = 3,
    SET_VALUE = 4,
    SET_RSE_MODE = 5,
    GET_VALUE_DOUBLE = 6,
    SET_VALUE_DOUBLE = 7,
    SET_STRING = 8,
    GET_VALUE_FLOAT = 9,
    SET_VALUE_FLOAT = 10,
    SET_TARGET = 11,
    GET_TARGET = 12,
    GET_TASK_STATUS = 13
}

public struct DESCR_DATA
{
    public long addr;
    public long value;
    public double dvalue;
}

public struct VAR_DATA
{
    public int len;
    public TYPE_OP type;
    public string name;
    public DESCR_DATA data;
}
VAR_DATA vardata = new VAR_DATA();
    

Code in C# to send structure "vardata" to C server:

public  byte[] Struct2Bytes(CommonStructures.VAR_DATA vdata_struct)
    {
        byte[] b_arr = new byte[Marshal.SizeOf(vdata_struct)];
        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(vdata_struct));
        Marshal.StructureToPtr(vdata_struct, ptr, true);
        Marshal.Copy(ptr, b_arr, 0, Marshal.SizeOf(vdata_struct));
        return b_arr;
    }

public void Identify(Socket Socket2Server, CommonStructures.VAR_DATA vardata, IPEndPoint remoteEP)
    {

        Socket2Server.Connect(remoteEP);
        Socket2Server.RemoteEndPoint.ToString();

        vardata.name = "Device Name"; // new char[100];
        vardata.len = 105;
        vardata.data.dvalue = 16.6;
        vardata.data.addr = 11;
        vardata.data.value = 666;
        vardata.type = CommonStructures.TYPE_OP.IDENTIFY;
        
        int bytesSent = Socket2Server.Send(Struct2Bytes(vardata));

        Socket2Server.Shutdown(SocketShutdown.Both);
        Socket2Server.Close();

    }

CodePudding user response:

You have quite a few mistakes in your struct definitions:

  • long in C is 32-bit, which is int in C#
  • name is char[100], which is a fixed-size array of ANSI characters. So it should have ByValTStr applied to it, and VAR_DATA needs CharSet.Ansi
public struct DESCR_DATA
{
    public int addr;
    public int value;
    public double dvalue;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct VAR_DATA
{
    public int len;
    public TYPE_OP type;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
    public string name;
    public DESCR_DATA data;
}

You don't need to marshal the struct yourself (your marshalling code is leaking HGlobals anyway). Instead just pass it as a ref parameter

 public void Identify(Socket Socket2Server, CommonStructures.VAR_DATA vardata, IPEndPoint remoteEP)
 {

     Socket2Server.Connect(remoteEP);
     Socket2Server.RemoteEndPoint.ToString();

     vardata.name = "Device Name";
     vardata.len = 105;
     vardata.data.dvalue = 16.6;
     vardata.data.addr = 11;
     vardata.data.value = 666;
     vardata.type = CommonStructures.TYPE_OP.IDENTIFY;
     
     int bytesSent = Socket2Server.Send(ref vardata);

     Socket2Server.Shutdown(SocketShutdown.Both);
     Socket2Server.Close();

 }

If you really need to use a byte[] array, you must make sure to release the HGlobal in a finally

 public  byte[] Struct2Bytes(CommonStructures.VAR_DATA vdata_struct)
 {
     byte[] b_arr = new byte[Marshal.SizeOf(vdata_struct)];
     IntPtr ptr = IntPtr.Zero;
     try
     {
         ptr = Marshal.AllocHGlobal(Marshal.SizeOf(vdata_struct));
         Marshal.StructureToPtr(vdata_struct, ptr, true);
         Marshal.Copy(ptr, b_arr, 0, Marshal.SizeOf(vdata_struct));
     }
     finally
     {
         Marshal.FreeHGlobal(ptr);
     }
     return b_arr;
 }

I must say, I don't understand why you are using C code here anyway. C# has a full complement of library classes for sockets.

  • Related