为二进制结构体添加CustomMarshaler字段时抛出异常的技术咨询
解决FILE_HEADER结构体封送DateTime字段的异常问题
你的核心问题在于:结构体中使用引用类型+自定义封送器的方式,会导致CLR无法正确计算结构体的大小和字段偏移——因为引用类型字段默认会被视为托管指针(4/8字节),而非自定义封送器声明的8字节,最终触发Marshal的异常。
下面是最稳妥的解决方案,无需自定义封送器,利用系统原生类型实现需求:
方案:使用系统自带FILETIME结构体+转换属性
直接使用System.Runtime.InteropServices.FILETIME(CLR原生支持的可封送结构体)来映射8字节的原生FILETIME字段,再通过属性封装实现与DateTime的转换。这样既保证结构体布局完全符合要求,又能方便地使用DateTime类型。
完整代码示例:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1, Size = 4096)] public struct FILE_HEADER { // 保留你的其他字段 public UInt32 ExampleUInt32; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string ExampleString; // 用系统原生FILETIME映射原生8字节字段 public System.Runtime.InteropServices.FILETIME LastWritten; public System.Runtime.InteropServices.FILETIME Created; // 通过属性封装DateTime的转换逻辑 public DateTime LastWrittenDateTime { get { // 组合dwHighDateTime和dwLowDateTime为Int64,再转DateTime long fileTimeValue = ((long)LastWritten.dwHighDateTime << 32) | (uint)LastWritten.dwLowDateTime; return DateTime.FromFileTime(fileTimeValue); } set { // 将DateTime转为Int64,拆分到FILETIME的两个字段 long fileTimeValue = value.ToFileTime(); LastWritten.dwHighDateTime = (int)(fileTimeValue >> 32); LastWritten.dwLowDateTime = (int)(fileTimeValue & 0xFFFFFFFF); } } public DateTime CreatedDateTime { get { long fileTimeValue = ((long)Created.dwHighDateTime << 32) | (uint)Created.dwLowDateTime; return DateTime.FromFileTime(fileTimeValue); } set { long fileTimeValue = value.ToFileTime(); Created.dwHighDateTime = (int)(fileTimeValue >> 32); Created.dwLowDateTime = (int)(fileTimeValue & 0xFFFFFFFF); } } }
为什么原方案会失败?
你使用自定义封送器的思路存在本质问题:
- 结构体中的引用类型(你的
FILETIME类)字段,CLR默认会将其解析为托管指针(32位系统4字节,64位系统8字节),而非自定义封送器声明的8字节数据。 Marshal.SizeOf或OffsetOf在计算结构体布局时,不会调用自定义封送器的GetNativeDataSize()方法,而是直接按指针大小计算,导致结构体总大小与你声明的Size=4096不匹配,最终抛出异常。- 官方文档明确说明
CustomMarshaler仅适用于方法参数/返回值,而非结构体字段,这也是原方案不可行的根本原因。
内容的提问来源于stack exchange,提问作者Gaston
相关产品推荐
相关产品推荐

