简体   繁体   中英

Convert a C++ struct to C#

I am writing a C# application that communicates with an old C DLL. I looked at the header of the C DLL and saw 2 structures that I need to create in C#:

typedef struct
{
    char    user_field_name[MAXLENGTH];
    WORD    user_field_length;
    char    user_field_contents[80];
    char    user_field_requested[1];
    char    user_field_clear[1];
    char    user_field_attribute[1];
} U_FIELD_STRUCT;
typedef U_FIELD_STRUCT FAR * P_U_FIELD_STRUCT;

typedef struct
{
    char                user_begin_literal[7];
    char                user_screen_name[MNFRMSCRNAMELENGTH];
    char                key_to_be_sent[256]; 
    WORD                num_of_user_fields;
    U_FIELD_STRUCT      user_field[MAXFIELDCOUNT];
    char                user_end_literal[7];
} USER_FIELD_STRUCT;

In my C# class, I wrote them as follows:

    public const Int32 MAXLENGTH = 51;
    public const Int32 MAXTAGLENGTH = 80;
    public const Int32 MNFRMSCRNAMELENGTH = 5;
    public const Int32 MAXFIELDCOUNT = 100;
    public const Int32 MAXTAGCOUNT = 40;
    public const Int32 MAXSCREENCOUNT = 150;

    public const Int32 NO_SCREEN_DATA = 99;

    [StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Ansi)]
    public struct U_FIELD_STRUCT
    {
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=MAXLENGTH)]
        public char [] user_field_name;
        public short user_field_length;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=80)]
        public char [] user_field_contents;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=1)]
        public char [] user_field_requested;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=1)]
        public char [] user_field_clear;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=1)]
        public char [] user_field_attribute;
    };

    [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
    public struct USER_FIELD_STRUCT
    {
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=7)]
        public char [] user_begin_literal;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=MNFRMSCRNAMELENGTH)]
        public char [] user_screen_name;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=256)]
        public char [] key_to_be_sent;
        public short num_of_user_fields;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=MAXFIELDCOUNT)]
        public U_FIELD_STRUCT [] user_field;
        [ MarshalAs( UnmanagedType.ByValArray, SizeConst=7)]
        public char [] user_end_literal;
    };

When I initialize the struct in C#, I do it as follows:

        USER_FIELD_STRUCT UserFieldsData = new USER_FIELD_STRUCT();
        UserFieldsData.user_begin_literal = new char[7];
        UserFieldsData.user_screen_name = new char[MNFRMSCRNAMELENGTH];
        UserFieldsData.key_to_be_sent = new char[256];
        UserFieldsData.user_field = new U_FIELD_STRUCT[MAXFIELDCOUNT];
        UserFieldsData.user_end_literal = new char[7];
        for (int i = 0; i < MAXFIELDCOUNT; i++)
        {
            UserFieldsData.user_field[i].user_field_name = new char[MAXLENGTH];
            UserFieldsData.user_field[i].user_field_contents = new char[80];
            UserFieldsData.user_field[i].user_field_requested = new char[1];
            UserFieldsData.user_field[i].user_field_clear = new char[1];
            UserFieldsData.user_field[i].user_field_attribute = new char[1];
        }

The problem is that when I pinvoke my method, I am getting an error message (basically the DLL has to validate the strings, and it sends a message that the strings are not what they are supposed to be). So I installed VS6 and I ran the DLL in Debug mode. I took a look at the struct I was sending and the strings did have the data that I put in them (some of the char[] had no data and other had a combination of 2 of the fields.)

For example, the field if I set the fields like this:

user_begin_literal=”T0k0a10”
user_end_literal=”T0k0b10”
user_screen_name=”SC00”

When I look at the object in VS6:

user_begin_literal=” T0k0a10”
user_end_literal=””
user_screen_name=”SC00”

Did I mess up my struct design?

In C, a char is only 1 byte, but in C# it is 2 bytes.

In C# use sbyte to map to a C char (both are signed 8-bit values), and ushort to map to a C WORD (both are unsigned 16-bit values).

If you want to assign a string value to one of your struct's buffers, you should convert it to ASCII (or UTF8 if the native code can handle it), and then copy the bytes to the field:

void CopyStringToField(string value, sbyte[] field)
{
    int maxLength = field.Length;  // or field.Length - 1 if you need room for null terminator

    byte[] fieldValue = Encoding.ASCII.GetBytes(value);
    if (fieldValue.Length > maxLength)
        throw new ArgumentException("string too long for field.");

    int length = Math.Min(fieldValue.Length, maxLength);
    Array.Copy(fieldValue, 0, field, 0, length);

    // zero fill remaining bytes
    for (int i = length; i < field.Length; i++)
    {
        field[i] = 0;
    }
}

You need to restructure your C# char into a byte . Also, you need to declare the byte array as a fixed byte in order to successfully pass it to your DLL.

WORD should be labeled as ushort in C# .

It is also unclear how your struct is ordered in your DLL, but you may be OK with setting the ordering as

[StructLayout(LayoutKind.Sequential)]

We have no information on byte padding from you.

See also this question .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM