将二进制文件读入struct

时间:2015-06-09 20:17:54

标签: c# struct marshalling

我知道这已经得到了回答,但在阅读了其他问题后,我仍然没有解决方案。我有一个用以下C ++结构编写的文件:

typedef struct myStruct{
    char Name[127];
    char s1[2];
    char MailBox[149];
    char s2[2];
    char RouteID[10];
} MY_STRUCT;

我的方法是能够在结构中一次解析一个字段,但我的问题是我无法正确解析s1和MailBox。在该文件中,s1字段包含“\ r \ n”(二进制0D0A),这导致我的解析代码无法正确解析MailBox字段。这是我的解析代码:

[StructLayout(LayoutKind.Explicit, Size = 0x80 + 0x2 + 0x96)]
unsafe struct MY_STRUCT
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
    public string Name;

    [FieldOffset(0x80)]
    public fixed char s1[2];

    /* Does not work, "Could not load type 'MY_STRUCT' ... because it contains an object field at offset 130 that is incorrectly aligned or overlapped by a non-object field." */
    [FieldOffset(0x80 + 0x2)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x96)]
    public string MailBox;
}

如果我注释掉最后一个字段并将struct的大小减小到0x80 + 0x2,它将对前两个变量正常工作。

有一点需要注意的是,Name和Mailbox字符串包含空终止字符,但由于s1没有空终止字符,所以它似乎搞乱了解析器,但我不知道为什么因为我看起来代码明确告诉Marshaler结构中的s1字段只是一个固定的2-char缓冲区,而不是以null结尾的字符串。

这是我的测试数据的图片(在代码中我搜索过BinaryReader中的第一行,因此“Name”从0x0开始,而不是0x10)。 enter image description here

3 个答案:

答案 0 :(得分:1)

这是一种方式,它不使用unsafe(也不是特别优雅/高效)

using System.Text;
using System.IO;

namespace ReadCppStruct
{
/*
 typedef struct myStruct{
    char Name[127];
    char s1[2];
    char MailBox[149];
    char s2[2];
    char RouteID[10];
    } MY_STRUCT;
 */
class MyStruct
{
    public string Name { get; set; }
    public string MailBox { get; set; }
    public string RouteID { get; set; }
}

class Program
{
    static string GetString(Encoding encoding, byte[] bytes, int index, int count)
    {
        string retval = encoding.GetString(bytes, index, count);
        int nullIndex = retval.IndexOf('\0');
        if (nullIndex != -1)
            retval = retval.Substring(0, nullIndex);
        return retval;
    }

    static MyStruct ReadStruct(string path)
    {
        byte[] bytes = File.ReadAllBytes(path);

        var utf8 = new UTF8Encoding();

        var retval = new MyStruct();
        int index = 0; int cb = 127;
        retval.Name = GetString(utf8, bytes, index, cb);
        index += cb + 2;
        cb = 149;
        retval.MailBox = GetString(utf8, bytes, index, cb);
        index += cb + 2;
        cb = 10;
        retval.RouteID = GetString(utf8, bytes, index, cb);

        return retval;
    }       // http://stackoverflow.com/questions/30742019/reading-binary-file-into-struct
    static void Main(string[] args)
    {
        MyStruct ms = ReadStruct("MY_STRUCT.data");
    }
}
}

答案 1 :(得分:0)

您的结构大小未正确添加。 MailBox大小为0x95,如MY_STRUCT中所列,而不是0x96,因为您在C#代码中调用它。

答案 2 :(得分:0)

以下是我如何让它为我工作:

    public static unsafe string BytesToString(byte* bytes, int len)
    {
        return new string((sbyte*)bytes, 0, len).Trim(new char[] { ' ' }); // trim trailing spaces (but keep newline characters)
    }

    [StructLayout(LayoutKind.Explicit, Size = 127 + 2 + 149 + 2 + 10)]
    unsafe struct USRRECORD_ANSI
    {
        [FieldOffset(0)]
        public fixed byte Name[127];

        [FieldOffset(127)]
        public fixed byte s1[2];

        [FieldOffset(127 + 2)]
        public fixed byte MailBox[149];

        [FieldOffset(127 + 2 + 149)]
        public fixed byte s2[2];

        [FieldOffset(127 + 2 + 149 + 2)]
        public fixed byte RouteID[10];
     }

在解析结构之后,我可以通过调用BytesToString方法来访问字符串,例如string name = BytesToString(record.Name, 127);

我注意到我不需要在StructLayout中使用Size属性,我不确定保留或删除它的最佳做法是什么?