Marshal.PtrToStructure抛出AccessViolationException [英] Marshal.PtrToStructure throwing AccessViolationException

查看:213
本文介绍了Marshal.PtrToStructure抛出AccessViolationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个结构:

    [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;
};

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

And I'm trying to read bytes from a file and copy those bytes to the above struct using Marshal and a GCHandle, my code is as follows:

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:尝试读或写受保护的内存

But I'm getting an AccessViolationException : attempt to read or write protected memory

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

I have no idea why this exception is thrown.

推荐答案

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

I didn't see the bug immediately and wrote a little test program to repro the problem. Using binary search to find the issue, commenting half the fields out repeatedly until I narrowed it down to:

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

这不能工作,pinvoke marshaller假定为​​ string 来自C字符串, char * 。这不可能纠正从文件读取的数据,它不能包含有效的指针。 AccessViolation在尝试解除引用指针时被触发。

That cannot work, the pinvoke marshaller assumes the default marshaling for string is from a C string, char*. That cannot possibly correct for data that you read from a file, it can never contain valid pointers. The AccessViolation is triggered when it tries to dereference the pointer.

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

There are no hints in the question to guess how the string was actually serialized to the file. The normal way is:

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

如有必要,使用十六进制查看器来找出SizeConst的正确值。如果编码是不寻常的(不是系统默认页面),那么你必须声明它为byte []并使用正确的编码转换它。

Use a hex viewer if necessary to figure out the proper value of SizeConst. If the encoding is unusual (not the system default page) then you have to declare it as byte[] and use the proper Encoding to convert it.

这篇关于Marshal.PtrToStructure抛出AccessViolationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆