You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为二进制结构体添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 08:06:22