Marshalled C字符串缺少字符

时间:2013-01-31 17:43:32

标签: c# marshalling

我在编组C字符数组时遇到问题。我有以下C#结构:

[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi), Serializable]
internal struct Header
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
    [FieldOffset(0)]
    public string header;

    [FieldOffset(4)]
    public int version;

    [FieldOffset(8)]
    public int diroffset;

    [FieldOffset(12)]
    public int direntries;
}

以及以下代码从流中读取此结构:

public static T ReadStruct<T>(this Stream stream) where T : struct
{
    var sz = Marshal.SizeOf(typeof(T));
    var buffer = new byte[sz];
    stream.Read(buffer, 0, sz);

    var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);

    var structure = (T) Marshal.PtrToStructure(
        pinnedBuffer.AddrOfPinnedObject(), typeof(T));

    pinnedBuffer.Free();
    return structure;
}

现在我的问题是header字段在读取结构后错过了一个字符。从中读取结构的文件包含四个字节VPVP,但在ReadStruct读取结构后,标题字符串仅包含VPV。如果我看一下调试器中read函数中的字节数组,那么该数组包含的值为86,80,86,80,即VPVP。我也尝试将LayoutKind.Sequential用于StructLayout,但这并未改变任何内容。

我做错了什么或为什么我的字符串中缺少一个字符?

1 个答案:

答案 0 :(得分:3)

你遇到的问题在于结构定义,而不是将字节写入它。

问题就出在这里:

[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]

正如你所说的那样,你会写出VPVP这个长度为4个字符的文字。然而,事实并非如此。在C中,您可以将字符串声明为:

char mystring[] = { 'V', 'P', 'V', 'P', '\0' };

您需要在结尾处使用空字符(\0)来标记字符串的结尾。编组时需要考虑到这一点,因为你需要为“null终结符字节”保留空间,如果不这样做,C#字符串会在可用内存中为你添加它,所以它会吞掉你的最后一个字符。因此,如果您要使用以null结尾的字符串,则必须使其长度为5。


编辑:这是一个更好的解决方案,您不必担心空终止符,只需使用char[](并且还可以保持魔术的16字节大小) ):

[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi), Serializable]
internal struct Header
{
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
    [FieldOffset(0)]
    private char[] headerCharArray;

    public string header
    {
        get { return new string(headerCharArray); }
        set
        {
            if (value.Length == 4)
            {
                headerCharArray = value.ToArray();
            }
            else
            {
                throw new InvalidOperationException("String length was not 4.");
            }
        }
    }

    [FieldOffset(4)]
    public int version;

    [FieldOffset(8)]
    public int diroffset;

    [FieldOffset(12)]
    public int direntries;
}

这样char[]存储在内存中,您可以通过属性将其作为字符串访问,而不会占用结构本身的任何内存。