【问题标题】:Marshal.PtrToStructure throwing AccessViolationExceptionMarshal.PtrToStructure 抛出 AccessViolationException
【发布时间】:2014-10-09 10:27:10
【问题描述】:

我有这个结构:

    [StructLayout(LayoutKind.Sequential)]
    public struct IS
    {
    public UInt32 ID; 
    public UInt32 Quality; 
    public UInt32 Flags;
    public UInt32 Flags2;     
    public UInt32 ContainerSlots; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public Int32[] ItemStatType;  
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public UInt32[] ItemStatValue;    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public Int32[] ItemStatUnk1;    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public Int32[] ItemStatUnk2;       
    public UInt32 ScalingStatDistribution; 
    public UInt32 DamageType;      
    public UInt32 Delay;      
    public float RangedModRange;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellId;          
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellTrigger;       
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellCharges;  
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellCooldown;   
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellCategory;     
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public Int32[] SpellCategoryCooldown;
    public UInt32 Bonding; 
    public string Name;       
    public string Name2;                  
    public string Name3;         
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public UInt32[] Color;  
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public UInt32[] Content;
};

我正在尝试从文件中读取字节并使用 Marshal 和 GCHandle 将这些字节复制到上面的struct,我的代码如下:

reader = BinaryReader.FromFile(fileName);
m_rows = new List<IS>();
int size = Marshal.SizeOf(typeof(IS));
if(reader.BaseStream.Length < size)
  return;
byte[] buffer = new byte[size];
buffer = reader.ReadBytes(size);
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
m_rows.Add((IS)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(IS)));
handle.Free();

但我收到了AccessViolationException : attempt to read or write protected memory

我不知道为什么会抛出这个异常。

【问题讨论】:

    标签: c# copy marshalling


    【解决方案1】:

    我没有立即看到错误并编写了一个小测试程序来重现问题。使用二进制搜索来查找问题,重复注释一半的字段,直到我将其缩小到:

    [StructLayout(LayoutKind.Sequential)]
    public struct IS {
        public string Name;
    }
    

    这行不通,pinvoke 编组器假定 string 的默认编组来自 C 字符串 char*。这不可能更正您从文件中读取的数据,它永远不会包含有效的指针。当它试图取消引用指针时会触发 AccessViolation。

    问题中没有提示猜测字符串是如何实际序列化到文件的。 正常的方式是:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct IS {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
        public string Name;
    };
    

    如有必要,请使用十六进制查看器来确定 SizeConst 的正确值。如果编码异常(不是系统默认页面),则必须将其声明为 byte[] 并使用正确的 Encoding 进行转换。

    【讨论】:

      【解决方案2】:

      正如您所理解的,访问冲突是由于尝试读取未分配的内存或正在释放的内存,您可能需要检查以下堆栈溢出发布:

      AccessViolationException when Marshal.PtrToStructure fires

      这将托管结构和本地结构的大小之间的差异作为问题的原因,本质上您需要在编组期间提供偏移量以匹配结构的托管分配和本地分配之间的差异。

      也检查此帖子,其中用户添加了偏移量

      Access violation exception when use method Marshal.PtrToStructure in a loop.

      另一个解决方案的链接:

      http://www.codeproject.com/Questions/585390/AccessplusViolationplusException

      如果这没有帮助,那么使用 windbg 调试此类问题很容易,我可以列出详细信息,以防您需要这样做。还要在 VS 中启用 Win32 Aces 违规异常,它会在行抛出异常时中断并提供一些附加信息

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多