views:

70

answers:

2

I am packing bytes into a struct, and some of them correspond to a Unicode string. The following works fine for an ASCII string:

[StructLayout(LayoutKind.Sequential)]
private struct PacketBytes
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    public string MyString;
}

I assumed that I could do

[StructLayout(LayoutKind.Sequential)]
private struct PacketBytes
{
    [MarshalAs(UnmanagedType.LPWStr, SizeConst = 32)]
    public string MyString;
}

to make it Unicode, but that didn't work (the field value was empty and the other fields had incorrect values, indicating that the byte unpacking was messed up). (Since this field is part of a struct with other fields, which I've omitted for clarity, I can't simply change the CharSet of the containing struct.)

Any idea what I'm doing wrong?

Here is the input (64 bytes, little endian):

31:00:31:00:32:00:33:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

The output should be the Unicode string "1123".

+2  A: 

I would do this by declaring a nested structure for the string type. The "inner" structure can declare its CharSet. This is similar to the solution on my blog: http://nitoprograms.blogspot.com/2010/02/interop-multidimensional-arrays-of.html

e.g.:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct StringSizeConst32AsString
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    private string Value;

    public static implicit operator string(StringSizeConst32AsString source)
    {
        return source.Value;
    }

    public static implicit operator StringSizeConst32AsString(string source)
    {
        // Note that longer strings would be silently truncated
        //  if we didn't explicitly check this.
        if (source.Length >= 32)
            throw new Exception("String too large for field: " + source);

        return new StringSizeConst32AsString { Value = source };
    }
}
Stephen Cleary
Nice! See my answer for the way I implemented your solution.
Pat
A: 

Credit to @StephenCleary. My full solution is this:

[StructLayout(LayoutKind.Sequential)]
private struct PacketBytes
{
    [MarshalAs(UnmanagedType.Struct)]
    public UnicodeString MyString;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
private struct UnicodeString
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Value;

    public static implicit operator string(UnicodeString value)
    {
        return value.Value;
    }
}
Pat
Again, in case anyone is wondering why I didn't just put the CharSet on the top struct, it my actual code there are other fields in that struct that need to use the ASCII CharSet. This solution lets you do both.
Pat