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

Unsafe类能否实现Marshal.PtrToStructure的结构体指针读取功能?

问题:Unsafe类读取结构体失败,能否实现Marshal.PtrToStructure的等效功能?

尝试用Unsafe类方法从指针读取结构体时全部失败,调试器显示“Unable to read memory”,但Marshal.PtrToStructure可以正常工作:

var info1 = Marshal.PtrToStructure<DbgHelp.SYMBOL_INFO_V>(pSymInfo); // 正常工作
var info2 = Unsafe.AsRef<DbgHelp.SYMBOL_INFO_V>((void*)pSymInfo); // 无法读取内存
var info3 = Unsafe.Read<DbgHelp.SYMBOL_INFO_V>((void*)pSymInfo); // 无法读取内存
var info4 = Unsafe.ReadUnaligned<DbgHelp.SYMBOL_INFO_V>((void*)pSymInfo); // 无法读取内存

补充说明:

  • pSymInfo是IntPtr类型
  • SYMBOL_INFO_V结构体定义:
public struct SYMBOL_INFO_V
{
    public uint SizeOfStruct;
    public uint TypeIndex;
    public ulong Reserved0;
    private ulong Reserved1;
    public uint Index;
    public uint Size;
    public ulong ModBase;
    public SYMFLAG Flags; // enum, uint
    public ulong Value;
    public ulong Address;
    public uint Register;
    public uint Scope;
    public uint Tag;
    public uint NameLen;
    public uint MaxNameLen;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
    public string Name;
}

解答

可以实现,但需要调整结构体定义,因为Unsafe类和Marshal的内存处理逻辑完全不同:

核心原因

Marshal.PtrToStructure会自动处理[MarshalAs(UnmanagedType.ByValTStr)]标记的字段,把非托管内存里的固定长度字符数组转换为托管string。但Unsafe类是直接按内存布局拷贝值类型,不做托管/非托管的类型转换——而C#的string是托管引用类型,非托管内存里的是原始字符数据,不是string对象的引用,直接读取会导致内存访问错误。

解决步骤

  1. 修改结构体定义,替换string为fixed字符数组
    把Name字段从string改成fixed char数组,让结构体的内存布局和非托管端完全匹配:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] // 匹配非托管字符集,按需调整为Ansi
    public struct SYMBOL_INFO_V
    {
        public uint SizeOfStruct;
        public uint TypeIndex;
        public ulong Reserved0;
        private ulong Reserved1;
        public uint Index;
        public uint Size;
        public ulong ModBase;
        public SYMFLAG Flags;
        public ulong Value;
        public ulong Address;
        public uint Register;
        public uint Scope;
        public uint Tag;
        public uint NameLen;
        public uint MaxNameLen;
        public fixed char Name[1]; // 用fixed数组代替string,SizeConst=1对应原定义的占位
    }
    

    注:SYMBOL_INFO的Name字段实际是可变长度缓冲区,SizeConst=1只是声明占位,实际分配内存时会按MaxNameLen调整大小,fixed数组的长度不影响Unsafe读取,后续可以按NameLen提取有效字符。

  2. 使用Unsafe类读取结构体
    调整结构体后,就可以用Unsafe方法正常读取:

    // 直接读取结构体
    var info = Unsafe.Read<DbgHelp.SYMBOL_INFO_V>((void*)pSymInfo);
    
    // 手动提取Name字符串(从fixed数组中读取到终止符或NameLen指定长度)
    string name;
    fixed (char* namePtr = info.Name)
    {
        name = new string(namePtr, 0, (int)info.NameLen);
    }
    
  3. 关键注意事项

    • 必须保证结构体的StructLayout属性和非托管端的内存对齐、字符集完全一致,否则读取的字段值会错位
    • Unsafe类操作的是非托管内存,需确保指针有效且内存未被释放,否则会引发内存访问异常

内容的提问来源于stack exchange,提问作者aybe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 15:20:26