将Byte数组编组为C#结构 [英] Marshaling a Byte array to a C# structure

查看:83
本文介绍了将Byte数组编组为C#结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一个C#项目,用于读取FAT32引导扇区和BPB,问题是我正在使用封送处理机制将字节数组转换为自定义FAT32数据结构. 我收到一条消息错误,说: 无法从程序集FAT32Management中加载类型FAT32Management.Fat32BootSector,版本为1.0.0.0,Culture = neutral,PublicKeyToken = null,因为它包含偏移量为3的对象字段,该对象字段错误地与非对象字段对齐或重叠.

I'm working on a C# project for reading FAT32 Boot Sector and BPB, The problem is that I'm using a marshaling mechanism to convert a byte array to a Custom FAT32 data structure. I get a message error saying : Could not load type 'FAT32Management.Fat32BootSector' from assembly 'FAT32Management, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 3 that is incorrectly aligned or overlapped by a non-object field.

我无法解决问题

这是代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace FAT32Management
{
    [StructLayout(LayoutKind.Explicit)]//, CharSet = CharSet.Ansi, Size = 96, Pack = 1)]
    public struct Fat32BootSector
    {
        #region Common Region With all FAT systems
        /// <summary>
        /// First 3 Bytes of the Jump insctructions.
        /// Offset 0x00  
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        [FieldOffset(0x00)]
        public byte[] JumpBootInstructions;

        /// <summary>
        /// 8 Bytes of the OemName
        /// Offset 0x03
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        [FieldOffset(0x03)]
        public string OemName;

        #region BIOS Parameter Block (BPB)
        /// <summary>
        /// 2 Bytes of the BytesPerSector parameter. The BIOS Paramter Block Starts here
        /// Offset 0x0b  
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x0b)]
        public ushort BpbBytesPerSector;

        /// <summary>
        /// 1 Byte containing the number of sectors per cluster. This must be a power of 2 from 1 to 128
        /// Offset 0x0d
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x0d)]
        public byte BpbSectorsPerCluster;

        /// <summary>
        /// 2 Bytes for the Bpb reserved sectors count, Usually 32 for FAT32.
        /// Offset 0x0e  
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x0e)]
        public ushort BpbReservedSectorsCount;

        /// <summary>
        /// 1 Byte Number of file allocation tables. Almost always 2.
        /// Offset 0x10
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x10)]
        public byte BpbFatCount;

        /// <summary>
        /// 2 Bytes, Maximum number of root directory entries. Only used on FAT12 and FAT16
        /// Offset 0x11
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x11)]
        public ushort BpbMaxRootDirectoriesCount;

        /// <summary>
        /// 2 Bytes, Total sectors (if zero, use 4 byte value at offset 0x20) used only for FAT12 AND FAT16 Systems
        /// Offset 0x13  
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x13)]
        public ushort BpbTotalSectors16;

        /// <summary>
        /// 1 Byte, the media descriptor 
        /// Offset 0x15
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x15)]
        public MediaDescriptor BpbMediaDescriptor;

        /// <summary>
        /// 2 Bytes, Sectors per File Allocation Table for FAT12/FAT16
        /// Offset 0x16
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x16)]
        public ushort BpbSectorsPerFat16;

        /// <summary>
        /// 2 Bytes, Sectors per track
        /// Offset 0x18
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x18)]
        public ushort BpbSectorsPerTrack;

        /// <summary>
        /// 2 Bytes Number of heads.
        /// Offset 0x1a
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x1a)]
        public ushort BpbNumberOfHeads;

        /// <summary>
        /// 4 Bytes Hidden sectors.
        /// Offset 0x1c
        /// </summary>
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(0x1c)]
        public uint BpbHiddenSectors;

        /// <summary>
        /// 4 Bytes, Total sectors (if greater than 65535; otherwise, see offset 0x13)
        /// Offset 0x20
        /// </summary>
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(0x20)]
        public uint BpbTotalSectors32;

        #endregion

        #endregion

        #region Extended BIOS Parameter Block: FAT32 Specific 

        /// <summary>
        /// 4 Bytes for the number of sectors occupied by ONE FAT. 
        /// Offset 0x24
        /// </summary>
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(0x24)]
        public uint FatSize32;

        /// <summary>
        /// 2 Bytes
        /// This field is only defined for FAT32 media and does not exist on FAT12 and FAT16 media.
        /// Bits 0-3 -- Zero-based number of active FAT. Only valid if mirroring is disabled.
        /// Bits 4-6 -- Reserved.
        /// Bit  7     -- 0 means the FAT is mirrored at runtime into all FATs.
        ///             -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
        /// Bits 8-15  -- Reserved.
        /// Offset 0x28
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x28)]
        public ushort ExtendedFlags;

        /// <summary>
        /// 2 Bytes for the file system version. The high byte is major revision number. Low byte is minor revision number. 
        /// Offset 0x2a
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        [FieldOffset(0x24)]
        public byte[] FileSystemVersion;

        /// <summary>
        /// 4 Bytes for the first cluster number of the root directory
        /// Offset 0x2c
        /// </summary>
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(0x2c)]
        public uint RootDirFirstClusterNumber;

        /// <summary>
        /// 2 Bytes for the Sector number of FS Information Sector.
        /// Offset 0x30
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x30)]
        public ushort FSInfoSectorNumber;

        /// <summary>
        /// 2 Bytes. If non-zero, indicates the sector number in the reserved area of the volume of a copy of the boot record. Usually 6.
        /// Offset 0x32
        /// </summary>
        [MarshalAs(UnmanagedType.U2)]
        [FieldOffset(0x32)]
        public ushort BackupBootSectorNumber;

        /// <summary>
        /// 12 Reserved Bytes.
        /// Offset 0x34
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray,SizeConst = 12)]
        [FieldOffset(0x34)]
        public byte[] Reserved;

        /// <summary>
        /// 1 Byte for the physical drive number.
        /// Offset 0x40
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x40)]
        public byte PhysicalDriveNumber;

        /// <summary>
        /// 1 Reserved byte.
        /// Offset 0x41
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x41)]
        public byte Reserved1;

        /// <summary>
        /// 1 Byte. The boot signature
        /// Offset 0x42
        /// </summary>
        [MarshalAs(UnmanagedType.U1)]
        [FieldOffset(0x42)]
        public byte ExtendedBootSignature;

        /// <summary>
        /// 4 Bytes for the volume serial number.
        /// Offset 0x43
        /// </summary>
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(0x43)]
        public uint VolumeID;

        /// <summary>
        /// 11 Byte for the volume label.
        /// Offset 0x47
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
        [FieldOffset(0x47)]
        public string VolumeLabel;

        /// <summary>
        /// 8 Bytes for the file system type string. 
        /// Offset 0x52
        /// </summary>
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
        [FieldOffset(0x52)]
        public string FileSystemType;

        #endregion

        /// <summary>
        /// Gets the boot sector for the specified drive.
        /// <remarks>The drive letter must have this pattern X: </remarks>
        /// </summary>
        /// <param name="driveLetter">The </param>
        /// <returns>The boot sector for the specified drive.</returns>
        public static Fat32BootSector GetBootSectorForDrive(string driveLetter)
        {
            byte[] bootSector = new byte[Marshal.SizeOf(typeof(Fat32BootSector))];
            string drive = @"\\.\" + driveLetter;
            IntPtr hardDiskPointer = SystemIOCalls.OpenFile(drive);
            // Seeks to the start of the partition
            SystemIOCalls.SeekAbsolute(hardDiskPointer, 0, 0);
            // Read the first reserved sector of the drive data (Boot Sector)
            // The data should be read with a chunk of 512 X byte.
            SystemIOCalls.ReadBytes(hardDiskPointer, bootSector, 512);
            SystemIOCalls.CloseHandle(hardDiskPointer);

            // Marshaling the bytes array to a valid struct
            GCHandle pinnedInfos = GCHandle.Alloc(bootSector, GCHandleType.Pinned);
            var infos = (Fat32BootSector)Marshal.PtrToStructure(pinnedInfos.AddrOfPinnedObject(), typeof(Fat32BootSector));
            pinnedInfos.Free();
            return infos;
        }
    }
}

请帮助!

推荐答案

显式结构布局和FieldOffsetAttribute不仅适用于编组,而且适用于CLR使用的运行时布局.如果您在不安全的代码中使用struct类型定义,或基于重叠数据进行其他假设(即有效地创建C样式的union类型),则这一点尤其重要.

Explicit struct layout and FieldOffsetAttribute apply not just to marshalling, but also to the runtime layout that the CLR uses. This is particularly important if you use struct type definitions in unsafe code, or make other assumptions based on overlapping data (i.e. effectively creating C-style union types).

作为将显式字段布局的这两种用法混合使用的副作用,禁止了违反CLR类型系统的布局:重叠的对象引用可以允许在运行过程中将对象引用构造为任意数据,而未对齐的对象引用影响GC实施可以使用的优化类型.

As a side-effect of conflating these two uses of explicit field layout, layouts that would violate the CLR type system are forbidden: overlapping object references can permit construction of object references to arbitrary data in the running process, while misaligned object references affect the kind of optimizations the GC implementation can use.

我相信您需要回退到使用固定字节数组存储字符串数据,并使用Encoding类自己对其进行解码.例如:

I believe you'll need to fall back to using fixed byte arrays for the string data, and decode it yourself using the Encoding class. For example:

[FieldOffset(0x03)]
public fixed byte OemName[8]

fixed字段需要不安全的上下文,并且需要一次被复制出一个元素,或被视为例如byte*使用不安全的代码.

fixed fields require unsafe context, and need to be either copied out one element at a time, or treated as e.g. byte* in unsafe code.

另一种方法是使用手动内存分配(Marhsal.AllocHGlobalbyte[])并将数据视为blob,可能使用BinaryReader读取.

Another approach would be to use manual memory allocation (either Marhsal.AllocHGlobal or byte[]) and treat the data as a blob, possibly read out of using a BinaryReader.

这篇关于将Byte数组编组为C#结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆