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.
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.
Related
I am converting c/c++ structures into C# standards.
C/C++ Structures:
typedef struct _sta_conn_info{
STA_CONNECT_STATE connect_state;//Enum
STA_ASSOC_STATE assoc_state;//Enum
unsigned char bssid[6];
unsigned char ssid[34];
unsigned long channel;
enum mode mode;//Enum
unsigned long signalStrength;
unsigned long noiseLevel;
STA_AUTH_ALG auth_alg;//enum
STA_ENCRYPT_ALG encrypt_alg;//enum
}STA_CONN_INFO;
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Respective C# structures:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _sta_conn_info
{
public _sta_connect_state connect_state;
public _sta_assoc_state assoc_state;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public char[] bssid ;//= new char[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public char[] ssid ;//= new char[34]
public uint channel;
public mode mode;
public uint signalStrength;
public uint noiseLevel;
public _sta_auth_alg auth_alg;
public _sta_encrypt_alg encrypt_alg;
}
QUERY STRUCT:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public byte[] Data;
};
I converted the data types using this >>>reference
Marshal.SizeOf() is working in WIN CE. I tested it.
If my structure conversion is fine then definitely Marshal.SizeOf() will work to get the size of the structure, but it is throwing exceptions and returning error code 87 in DeviceIoControl() API.
Can anyone clarify me about the conversions and let me know If I did anything wrong.
For bssid and ssid the C++ declarations are:
unsigned char bssid[6];
unsigned char ssid[34];
Now, unsigned char is a single byte and is typically used for byte arrays rather than text. So the C# should be:
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public byte[] bssid ;//= new byte[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public byte[] ssid ;//= new byte[34]
Your use of char in the C# is not correct because char is two bytes wide in C#.
In _NDISUIO_QUERY_OID where you have
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public byte[] Data;
I believe that you need to use ByValArray rather than ByValTStr. But as we have discussed in many of your recent questions, the exact meaning of this member is unclear. Is it really a fixed length byte array, or is it a variable length buffer? Do you have sample C++ code that works? That would settle the debate once and for all.
OK, from the header nuiouser.h header file I have this:
//
// Structure to go with IOCTL_NDISUIO_QUERY_OID_VALUE.
// The Data part is of variable length, determined by
// the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
#ifdef UNDER_CE
//
// In CE land app is allowed to query without having to do
// IOCTL_NDISUIO_OPEN_DEVICE
// Hence the device name to query argument needed..
// For app that does IOCTL_NDISUIO_OPEN_DEVICE this argument
// is then not necessary..
//
PTCHAR ptcDeviceName;
#endif
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Which tells you conclusively that Data is variable length. You'll need to allocate the struct with AllocHGlobal and do all the marshalling by hand I am afraid.
I want to write a plugin for a program. The program can only use C/C++ *.dll libraries. I want to write my plugin in C# though, so I thought I could just call my C# functions from the C++ dll through COM. This works fine but now I need to access a struct provided by the original program. In C++ this struct looks like this:
struct asdf{
char mc[64];
double md[10];
unsigned char muc[5];
unsigned char muc0 : 1;
unsigned char muc1 : 1;
unsigned char muc2 : 6;
unsigned char muc3;
another_struct st;
};
To be able to pass that struct to C# as a parameter I tried to built exactly the same struct in C#. I tried the following, but it gives me an access violation:
struct asdf{
char[] mc;
double[] md;
byte[] muc;
byte muc0;
byte muc1;
byte muc2;
byte muc3;
another_struct st;
};
What do I have to change?
If you want the arrays inline, you need to use fixed-size buffers. I'm assuming that the C char is a byte. The code to handle muc0, muc1, etc. will require some custom properties. You treat the entire thing like a byte.
struct asdf
{
public fixed byte mc[64];
public fixed double md[10];
public fixed byte muc[5];
private byte mucbyte;
// These properties extract muc0, muc1, and muc2
public byte muc0 { get { return (byte)(mucbyte & 0x01); } }
public byte muc1 { get { return (byte)((mucbyte >> 1) & 1); } }
public byte muc2 { get { return (byte)((mucbyte >> 2) & 0x3f); } }
public byte muc3;
public another_struct st;
};
I would change it slightly, use a string and make sure you init your arrays to the same size used in the C++ code when you use the struct in your program.
struct asdf{
string mc;
double[] md;
byte[] muc;
byte muc0;
byte muc1;
byte muc2;
byte muc3;
};
Simple question, but I can't find a straight answer: I want to have a char array of 128 bytes in my C structure. I am running this under 64bit Windows. I want to marshal this over to c#, using the following:
The C code:
typedef struct s_parameterStuct
{
int count;
char name[ 128 ];
} parameterStruct;
And the c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class parameterStuct
{
public int count;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public char[] name;
}
Since a char is 2 bytes in c#, should the SizeConst be 128 or 256. Both seem to work fine, but I know only one of them is correct.
The size would be 64, since 64 2-byte quantities have the same size as 128 1-byte ones.
I'd use a byte array for marshaling because otherwise you'll have to put up with splitting values in order to get the C chars (which are single bytes).
If you want to marshal to a C# char array (not sure why you would want to) then the size would be set to 64 on the C# end.
However, you should be marshaling to a byte array (size 128) and then converting the byte array to a .net string (assuming you want a string) using the appropriate Text.Encoding call.
I need to create a binary blob of empty data to PInvoke a native C++ dll that needs a unsigned char* of nulls.
The native C++ program is expecting a structure of data, and there's a nulled area of bytes in the middle, but in C# I can't just make a struct with an initialized byte[] in the middle.
My struct in C++ looks like this
struct myStruct
{
byte command;
byte returncode;
void* Source (a pointer to a string, rather a null term char*)
void* Destination (same thing)
byte filler [99]
byte options;
}
I've already figured that I can take a string and convert it to an array of bytes using myStruct.Source = (void*)Marshal.StringtToGlobalAnsi(source) (correct me if I'm wrong).
But I don't know how to fill out that empty array of bytes in the middle.
This is my C# struct so far.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Trans_s
{
public byte command;
public byte returnCode;
public void* pSource;
public void* pDest;
public byte* filler;
public byte options;
}
For the array of bytes, you need to mark the field in the C# struct as a byvalarray. The default marshaller uses LPArray if you do not.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 99)]
public byte[] filler;
You need to allocate the memory for filler when you create an instance of the struct.
No, the C++ code doesn't expect an unsigned char* of nulls. It expects that many padding bytes inside the struct.
The C# fixed keyword might help you here.
I have such a C++ structure:
typedef struct _FILE_OP_BLOCK
{
unsigned short fid; // objective file ID
unsigned short offset; // operating offset
unsigned char len; // buffer length(update)
// read length(read)
unsigned char buff[240];
} FILE_OP_BLOCK;
And now I want to map it in .Net. The tricky thing is that the I should pass a 2 byte array for fid, and integer for len, even though in C# fid is an unsigned short and len is an unsigned char
I wonder whether my structure ( in C#) below is correct?
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
public struct File_OP_Block
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 240)]
public char[] buff;
}
Your CharSet property on the [DllImport] attribute is definitely wrong, you need CharSet.Ansi to get the P/Invoke marshaller to convert it to a char[]. Declare the buff member as a string for easier usage. While declaring the fid member as a byte[] isn't wrong, I really don't see the point of it. That the unmanaged code copies a char[] into it is an implementation detail. Thus:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct File_OP_Block
{
public ushort fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 240)]
public string buff;
}
Given that the C++ short data type is actually two bytes, a two byte array should work. The integer sizes in C/C++ are not strictly defined, so the standard only says that a short is at least two bytes.
The C# char data type is a 16 bit unicode character, so that doesn't match the C++ char data type which is an 8 bit data type. You either need an attribute to specify how the characters are encoded into bytes, or use a byte array.
You might need an attribute to specify the packing, so that there is no padding between the members.