【问题标题】:Dealing With Odd FieldOffset处理奇数场偏移
【发布时间】:2014-06-03 06:46:21
【问题描述】:

我正在尝试将以下 C++ 结构移植到 C#。

#pragma pack(push,1)
struct FatBootSectorStruct {
    UCHAR  BS_jmpBoot[3];          // 0
    UCHAR  BS_OEMName[8];          // 3
    USHORT BPB_BytsPerSec;         // 11
    UCHAR  BPB_SecPerClus;         // 13
    USHORT BPB_RsvdSecCnt;         // 14
    UCHAR  BPB_NumFATs;            // 16
    USHORT BPB_RootEntCnt;         // 17
    USHORT BPB_TotSec16;           // 19
    UCHAR  BPB_Media;              // 21
    USHORT BPB_FATSz16;            // 22
    USHORT BPB_SecPerTrk;          // 24
    USHORT BPB_NumHeads;           // 26
    ULONG  BPB_HiddSec;            // 28
    ULONG  BPB_TotSec32;           // 32
    union {
        struct {
            UCHAR  BS_DrvNum;          // 36
            UCHAR  BS_Reserved1;       // 37
            UCHAR  BS_BootSig;         // 38
            ULONG  BS_VolID;           // 39
            UCHAR  BS_VolLab[11];      // 43
            UCHAR  BS_FilSysType[8];   // 54
            UCHAR  BS_Reserved2[448];  // 62
        } Fat16;
        struct {
            ULONG  BPB_FATSz32;        // 36
            USHORT BPB_ExtFlags;       // 40
            USHORT BPB_FSVer;          // 42
            ULONG  BPB_RootClus;       // 44
            USHORT BPB_FSInfo;         // 48
            USHORT BPB_BkBootSec;      // 50
            UCHAR  BPB_Reserved[12];   // 52
            UCHAR  BS_DrvNum;          // 64
            UCHAR  BS_Reserved1;       // 65
            UCHAR  BS_BootSig;         // 66
            ULONG  BS_VolID;           // 67
            UCHAR  BS_VolLab[11];      // 71
            UCHAR  BS_FilSysType[8];   // 82
            UCHAR  BPB_Reserved2[420]; // 90
        } Fat32;
    };
    USHORT Signature;              // 510
};

这就是我所拥有的:

[StructLayout(LayoutKind.Explicit, Size = 512, Pack=1)]
internal struct FATBootSector
{
    [FieldOffset(0)]
    public byte[] BS_jmpBoot; // 0
    [FieldOffset(3)]
    public byte[] BS_OEMName; // 3
    [FieldOffset(11)]
    public ushort BPB_BytsPerSec; // 11
    [FieldOffset(13)]
    public byte BPB_SecPerClus; // 13
    [FieldOffset(14)]
    public ushort BPB_RsvdSecCnt; // 14
    [FieldOffset(16)]
    public byte BPB_NumFATs; // 16
    [FieldOffset(17)]
    public ushort BPB_RootEntCnt; // 17
    [FieldOffset(19)]
    public ushort BPB_TotSec16; // 19
    [FieldOffset(21)]
    public byte BPB_Media; // 21
    [FieldOffset(22)]
    public ushort BPB_FATSz16; // 22
    [FieldOffset(24)]
    public ushort BPB_SecPerTrk; // 24
    [FieldOffset(26)]
    public ushort BPB_NumHeads; // 26
    [FieldOffset(28)]
    public ulong BPB_HiddSec; // 28
    [FieldOffset(32)]
    public ulong BPB_TotSec32; // 32

    // FAT16
    [FieldOffset(36)]
    public byte FAT16_BS_DrvNum; // 36
    [FieldOffset(37)]
    public byte FAT16_BS_Reserved1; // 37
    [FieldOffset(38)]
    public byte FAT16_BS_BootSig; // 38
    [FieldOffset(39)]
    public ulong FAT16_BS_VolID; // 39
    [FieldOffset(43)]
    public byte[] FAT16_BS_VolLab; // 43
    [FieldOffset(54)]
    public byte[] FAT16_BS_FilSysType; // 54
    [FieldOffset(62)]
    public byte[] FAT16_BS_Reserved2; // 62

    // FAT32
    [FieldOffset(36)]
    public ulong FAT32_BPB_FATSz32; // 36
    [FieldOffset(40)]
    public ushort FAT32_BPB_ExtFlags; // 40
    [FieldOffset(42)]
    public ushort FAT32_BPB_FSVer; // 42
    [FieldOffset(44)]
    public ulong FAT32_BPB_RootClus; // 44
    [FieldOffset(48)]
    public ushort FAT32_BPB_FSInfo; // 48
    [FieldOffset(50)]
    public ushort FAT32_BPB_BkBootSec; // 50
    [FieldOffset(52)]
    public byte[] FAT32_BPB_Reserved; // 52
    [FieldOffset(64)]
    public byte FAT32_BS_DrvNum; // 64
    [FieldOffset(65)]
    public byte FAT32_BS_Reserved1; // 65
    [FieldOffset(66)]
    public byte FAT32_BS_BootSig; // 66
    [FieldOffset(67)]
    public byte FAT32_BS_VolID; // 67
    [FieldOffset(71)]
    public byte[] BS_VolLab; // 71
    [FieldOffset(82)]
    public byte[] FAT32_BS_FilSysType; // 82
    [FieldOffset(90)]
    public byte[] FAT32_BPB_Reserved2; // 90

    [FieldOffset(510)]
    public ushort Signature;

}

问题在于字段偏移量 0 和 3。当我尝试在 C# 中加载结构时,我得到Could not load type 'FATBootSector' from assembly 'xxxx' because it contains an object field at offset 3 that is incorrectly aligned or overlapped by a non-object field.。我遇到了this previous question on SO,但这似乎没有帮助。

我也试过把它改成如下的顺序结构:

[StructLayout(LayoutKind.Sequential, Size = 512, Pack=1, CharSet=CharSet.Ansi)]
internal struct FATBootSector
{

    [MarshalAs(UnmanagedType.U1)]
    public byte BS_jmpBoot0; // 0
    [MarshalAs(UnmanagedType.U1)]
    public byte BS_jmpBoot1; // 0
    [MarshalAs(UnmanagedType.U1)]
    public byte BS_jmpBoot2; // 0

    [MarshalAs(UnmanagedType.LPStr, SizeConst=8)]
    public string BS_OEMName; // 3
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_BytsPerSec; // 11
    [MarshalAs(UnmanagedType.U1)]
    public byte BPB_SecPerClus; // 13
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_RsvdSecCnt; // 14
    [MarshalAs(UnmanagedType.U1)]
    public byte BPB_NumFATs; // 16
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_RootEntCnt; // 17
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_TotSec16; // 19
    [MarshalAs(UnmanagedType.U1)]
    public byte BPB_Media; // 21
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_FATSz16; // 22
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_SecPerTrk; // 24
    [MarshalAs(UnmanagedType.U2)]
    public ushort BPB_NumHeads; // 26
    [MarshalAs(UnmanagedType.U4)]
    public ulong BPB_HiddSec; // 28
    [MarshalAs(UnmanagedType.U4)]
    public ulong BPB_TotSec32; // 32

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 474)]
    public byte[] FAT1632Info;

    [MarshalAs(UnmanagedType.U2)]
    public ushort Signature;

}

我正在使用以下代码来获取结构:

[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool ReadFile(IntPtr hFile, [Out] IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped);

public bool GetPartitionDetails()
{
        uint BytesRead;

        IntPtr BootSectorPtr = Marshal.AllocHGlobal(512);
        PInvoke.FATBootSector BootSector;

        System.Threading.NativeOverlapped Overlapped = new System.Threading.NativeOverlapped();

        bool ret = PInvoke.ReadFile(this.Handle, BootSectorPtr, (uint)512, out BytesRead, ref Overlapped);

        BootSector = (PInvoke.FATBootSector)Marshal.PtrToStructure(BootSectorPtr, typeof(PInvoke.FATBootSector)); // causes access violation

        return true;
}

我希望我不必这样做,但我能想到的唯一选择是使用 IntPtr 遍历内存。有什么想法吗?

【问题讨论】:

  • public byte[] BS_jmpBoot; 是否在 C# 中声明了一个未知大小的数组或指向数组的指针?
  • 我尝试使用MarshalAs指定大小,固定字节,如上所示,将其分成3个单独的字节。

标签: c# c++ struct pinvoke offset


【解决方案1】:

这里有很多问题。首先你在滥用FieldOffset。当编译器无法为您布局结构时,您可以使用它。总是在你有工会的时候。不要在这里使用FieldOffset。当然不要预先指定尺寸。同样,让编译器这样做。对照本机版本进行检查。

然后你错误地处理了数组。您不需要fixed,但您确实需要说明数组的长度。结构的开头应如下所示:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct FATBootSector
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] BS_jmpBoot;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] BS_OEMName;
    ....
}

其余的问题是由错误的类型转换引起的。 C++ 中的ULONG 是 4 字节无符号类型。那是 C# 中的uint。您使用的是 8 字节宽的 ulong

这是结构的一个版本,它至少可以避免访问冲突:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct FATBootSector
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] BS_jmpBoot; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] BS_OEMName; 
    public ushort BPB_BytsPerSec;
    public byte BPB_SecPerClus; 
    public ushort BPB_RsvdSecCnt;
    public byte BPB_NumFATs; 
    public ushort BPB_RootEntCnt;
    public ushort BPB_TotSec16; 
    public byte BPB_Media; 
    public ushort BPB_FATSz16;
    public ushort BPB_SecPerTrk;
    public ushort BPB_NumHeads;
    public uint BPB_HiddSec; 
    public uint BPB_TotSec32; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 474)]
    public byte[] FAT1632Info;
    public ushort Signature;
}

要转换 FAT16/FAT32 联合,您可能需要使用fixed。或者有两个FATBootSector 类型。一个用于 FAT16,一个用于 FAT32。

【讨论】:

    【解决方案2】:

    我决定使用IntPtr 来填写结构字段。以下代码遍历内存并获取每个字段。

        internal struct FATBootSector
        {
            public byte[] BS_jmpBoot; // 0
            public string BS_OEMName; // 3
            public ushort BytesPerSector; // 11
            public byte SectorsPerCluster; // 13
            public ushort ReservedSectors; // 14
            public byte NumberOfFATs; // 16
            public ushort RootEntries; // 17
            public ushort TotalSectors16; // 19
            public byte MediaDescriptor; // 21
            public ushort SectorsPerFAT; // 22
            public ushort SectorsPerTrack; // 24
            public ushort Heads; // 26
            public uint HiddenSectors; // 28
            public uint TotalSectors32; // 32
    
            public FAT1632Info FAT1632Info;
    
            public ushort Signature;
    
            public FATBootSector(IntPtr ptr)
            {
                int i;
    
                this.BS_jmpBoot = new byte[3];
    
                for (i = 0; i < 3; i++)
                {
                    this.BS_jmpBoot[i] = Marshal.ReadByte(ptr);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                StringBuilder oemInfo = new StringBuilder(8);
    
                for (i = 0; i < 8; i++)
                {
                    char c = (char)Marshal.ReadByte(ptr);
                    oemInfo.Append(c);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.BS_OEMName = oemInfo.ToString();
    
                this.BytesPerSector = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.SectorsPerCluster = Marshal.ReadByte(ptr);
                ptr = IntPtr.Add(ptr, 1);
    
                this.ReservedSectors = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.NumberOfFATs = Marshal.ReadByte(ptr);
                ptr = IntPtr.Add(ptr, 1);
    
                this.RootEntries = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.TotalSectors16 = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.MediaDescriptor = Marshal.ReadByte(ptr);
                ptr = IntPtr.Add(ptr, 1);
    
                this.SectorsPerFAT = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.SectorsPerTrack = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.Heads = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
    
                this.HiddenSectors = (uint)Marshal.ReadInt32(ptr);
                ptr = IntPtr.Add(ptr, 4);
    
                this.TotalSectors32 = (uint)Marshal.ReadInt32(ptr);
                ptr = IntPtr.Add(ptr, 4);
    
                this.FAT1632Info = new PInvoke.FAT1632Info(ptr);
                ptr = IntPtr.Add(ptr, 474);
    
                this.Signature = (ushort)Marshal.ReadInt16(ptr);
                ptr = IntPtr.Add(ptr, 2);
            }
        }
    
        [StructLayout(LayoutKind.Sequential, Size=474, Pack=1)]
        internal struct FAT1632Info
        {
            // FAT16
            public byte FAT16_LogicalDriveNumber; // 36
            public byte FAT16_Reserved1; // 37
            public byte FAT16_ExtendedSignature; // 38
            public uint FAT16_PartitionSerialNumber; // 39
            public string FAT16_VolumeName; // 43
            public string FAT16_FSType; // 54
            public byte[] FAT16_Reserved2; // 62
    
            // FAT32
            public uint FAT32_SectorsPerFAT32; // 36
            public ushort FAT32_ExtFlags; // 40
            public ushort FAT32_FSVer; // 42
            public uint FAT32_RootDirStart; // 44
            public ushort FAT32_FSInfoSector; // 48
            public ushort FAT32_BackupBootSector; // 50
            public byte[] FAT32_Reserved1; // 52
            public byte FAT32_LogicalDriveNumber; // 64
            public byte FAT32_Reserved2; // 65
            public byte FAT32_ExtendedSignature; // 66
            public uint FAT32_PartitionSerialNumber; // 67
            public string FAT32_VolumeName; // 71
            public string FAT32_FSType; // 82
            public byte[] FAT32_Reserved3; // 90
    
            public FAT1632Info(IntPtr ptr)
            {
                int i;
                IntPtr startPtr = ptr;
    
                // FAT 16
                this.FAT16_LogicalDriveNumber = Marshal.ReadByte(ptr); // 0
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT16_Reserved1 = Marshal.ReadByte(ptr); // 1
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT16_ExtendedSignature = Marshal.ReadByte(ptr); // 2
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT16_PartitionSerialNumber = (uint)Marshal.ReadInt32(ptr); // 3
                ptr = IntPtr.Add(ptr, 4);
    
                StringBuilder volName16 = new StringBuilder(11);
    
                for (i = 0; i < 11; i++)
                {
                    char c = (char)Marshal.ReadByte(ptr);
                    volName16.Append(c);
    
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.FAT16_VolumeName = volName16.ToString();
    
                StringBuilder fileSystemType16 = new StringBuilder(8);
    
                for (i = 0; i < 8; i++)
                {
                    char c = (char)Marshal.ReadByte(ptr);
                    fileSystemType16.Append(c);
    
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.FAT16_FSType = fileSystemType16.ToString();
    
                this.FAT16_Reserved2 = new byte[448];
    
                for (i = 0; i < 448; i++)
                {
                    this.FAT16_Reserved2[i] = Marshal.ReadByte(ptr);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                // FAT32
                ptr = startPtr;
    
                this.FAT32_SectorsPerFAT32 = (uint)Marshal.ReadInt32(ptr); // 36, 4
                ptr = IntPtr.Add(ptr, 4);
    
                this.FAT32_ExtFlags = (ushort)Marshal.ReadInt16(ptr); // 40, 2
                ptr = IntPtr.Add(ptr, 2);
    
                this.FAT32_FSVer = (ushort)Marshal.ReadInt16(ptr); // 42, 2
                ptr = IntPtr.Add(ptr, 2);
    
                this.FAT32_RootDirStart = (uint)Marshal.ReadInt32(ptr); // 44, 4
                ptr = IntPtr.Add(ptr, 4);
    
                this.FAT32_FSInfoSector = (ushort)Marshal.ReadInt16(ptr); // 48, 2
                ptr = IntPtr.Add(ptr, 2);
    
                this.FAT32_BackupBootSector = (ushort)Marshal.ReadInt16(ptr); // 50, 2
                ptr = IntPtr.Add(ptr, 2);
    
                this.FAT32_Reserved1 = new byte[12];  // 52, 12
    
                for (i=0;i<12;i++) 
                {
                    this.FAT32_Reserved1[i] = Marshal.ReadByte(ptr);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.FAT32_LogicalDriveNumber = (byte)Marshal.ReadByte(ptr); // 64, 1
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT32_Reserved2 = (byte)Marshal.ReadByte(ptr); // 65, 1
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT32_ExtendedSignature = (byte)Marshal.ReadByte(ptr); // 66, 1
                ptr = IntPtr.Add(ptr, 1);
    
                this.FAT32_PartitionSerialNumber = (uint)Marshal.ReadInt32(ptr); // 67, 1
                ptr = IntPtr.Add(ptr, 4);
    
                StringBuilder volName32 = new StringBuilder(11); // 71, 11
    
                for (i = 0; i < 11; i++)
                {
                    char c = (char)Marshal.ReadByte(ptr);
                    volName32.Append(c);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.FAT32_VolumeName = volName32.ToString();
    
                StringBuilder fileSystemType32 = new StringBuilder(8);   // 82, 8
    
                for (i = 0; i < 8; i++)
                {
                    char c = (char)Marshal.ReadByte(ptr);
                    fileSystemType32.Append(c);
                    ptr = IntPtr.Add(ptr, 1);
                }
    
                this.FAT32_FSType = fileSystemType32.ToString();
    
                this.FAT32_Reserved3 = new byte[420]; // 90, 420
    
                for (i = 0; i < 420; i++)
                {
                    this.FAT32_Reserved3[i] = Marshal.ReadByte(ptr);
                    ptr = IntPtr.Add(ptr, 1);
                }
            }
        }
    

    不过,我确实喜欢 David 留下的关于为什么它不能正常工作的很好解释,所以我会接受它作为答案,并将其留在这里,以备将来其他人遇到我遇到的同样问题时使用尝试在 C# 中读取 FAT 引导扇区。

    【讨论】:

    • 把它写成一个结构体并避免所有这些粗糙的代码真的不是那么难。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    相关资源
    最近更新 更多