2

我在编组 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. 如果我查看调试器中读取函数中的字节数组,则该数组包含值 86、80、86、80,即VPVP. 我也尝试使用LayoutKind.Sequentialfor ,StructLayout但这并没有改变任何东西。

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

4

1 回答 1

4

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

问题就在这里:

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

正如您所说,您正在写出 text VPVP,您认为它有 4 个字符长。然而,事实并非如此。在 C 中,您可以这样声明字符串:

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

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


编辑:这是一个更好的解决方案,您不必担心空终止符,您只需使用 a 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[]存储在内存中,您可以通过属性将其作为字符串访问,该属性不占用结构本身的任何内存。

于 2013-01-31T18:16:59.747 回答