处理奇数字段偏移
Dealing With Odd FieldOffset
我正在尝试将下面的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.
。我在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
迭代内存。什么好主意吗?
这里有不少问题。首先,你在滥用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
。您使用的ulong
是8字节宽。
下面是该结构体的一个版本,至少可以避免访问冲突:
[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。
我决定使用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引导扇区
- 如何用数字处理log(0)
- 如何在 WindowProc 处理程序中区分箭头键和数字键盘?
- std::sort 如何处理重复的数字?
- 如何安全地将有符号字段从uint32_t提取到有符号数字(int或uint32_t)中
- 引发未经处理的异常:简单 C++ 程序中的读取访问冲突,动态增加数组长度以存储数字
- 如何检查字段中输入的数字是否重复
- 无法在按下数字和数字键时处理Qt C++中的QKey事件
- 为什么 CSpinButtonCtrl 不能正确处理大于 1000 的数字?
- 具有7个段显示的数字时钟,带有PIC18F445K22
- 在处理C 中的数字格式时,在SetPrecision中使用固定关键字
- 为什么这段代码输出这么多数字
- 当数字可能超出C++中特定数据类型的范围时如何处理异常?
- Visual Studio 2015中的命令参数字段
- 如何在 CORBA 中获取和设置类字段?(处理 CORBA 对象的序列)
- 初始化成员函数字段
- FP号码的指数字段不是我预期的,为什么?
- c++在模块间共享数组元素,仅对少数字段具有const性
- 数字信号处理例程中的Node.js
- Setw()不影响读取整数字段
- 如何设置ostream的指数字段的宽度