Byte array for structure

I am having trouble converting the string parts of a byte array.

My structure looks like this:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Message
{
    public int id;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string text;
}

      

Creating an array of test bytes:

private static byte[] CreateMessageByteArray()
{
    int id = 69;
    byte[] intBytes = BitConverter.GetBytes(id);

    string text = "test";
    byte[] stringBytes = GetBytes(text);

    IEnumerable<byte> rv = intBytes.Concat(stringBytes);

    return rv.ToArray();
}

      

Method for converting my bytearray to structure:

static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    var result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return result;
}

      

When I call ByteArrayToStructure

with the result from CreateMessageByteArray()

, I get a structure with id = 60 and text = "t".

Why don't I get the whole string like "test"?

Edit: This is the code I forgot to follow:

    static byte[] GetBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }

      

+3


source to share


3 answers


The problem is this line:

byte[] stringBytes = GetBytes(text);

      

How do you convert a string to a byte array? You are probably using Unicode encoding, which will store each character as two bytes, and since your string is in the ASCII set, every other byte will be zero:

byte[] stringBytes = new UnicodeEncoding().GetBytes(text);
// will give you { 't', '\0', 'e', '\0', 's', '\0', 't', '\0' }

      

These zeros mislead the sorting mechanism, assuming they are terminal characters, and therefore the line ends immediately after 't'

.



You can use ASCII encoding instead (which stores one byte per character):

byte[] stringBytes = new ASCIIEncoding().GetBytes(text);
// will give you { 't', 'e', 's', 't' }
// but will lose non-ASCII character information

      

Or you can use UTF8 encoding (which is variable length):

byte[] stringBytes = new UTF8Encoding().GetBytes(text);
// will give you { 't', 'e', 's', 't' }    
// and retain non-ASCII character information, but it somewhat
// trickier to rebuild the string correctly in case of non-ASCII
// information present

      

+4


source


In addition to the other two answers, if you want the string in the field to text

always be Unicode, you can include CharSet = CharSet.Unicode

in your attribute[StructLayout]



+4


source


The GetBytes method may not work as you expect. This linqpad works fine for me:

void Main()
{
    var result = ByteArrayToStructure<Message>(CreateMessageByteArray());
    result.Dump();
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Message
{
    public int id;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string text;
}

private static byte[] CreateMessageByteArray()
{
    int id = 69;
    byte[] intBytes = BitConverter.GetBytes(id);

    string text = "test";
    byte[] stringBytes = Encoding.UTF8.GetBytes(text);

    IEnumerable<byte> rv = intBytes.Concat(stringBytes);

    return rv.ToArray();
}

static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    var result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return result;
}

      

Output:

id    69 
text  test 

      

+1


source







All Articles