我正在尝试直接向/从文件写/读多字节数组,并建议使用PInvoke WriteFile / ReadFile。
基本上我的阅读代码现在看起来像这样:
[DllImport("kernel32.dll", SetLastError = true)]
static extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead,
IntPtr numBytesRead, System.Threading.NativeOverlapped* overlapped);
..<cut>..
byte[,,] mb = new byte[1024,1024,1024];
fixed(byte * fb = mb)
{
FileStream fs = new FileStream(@"E:\SHARED\TEMP", FileMode.Open);
int bytesread = 0;
ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), new IntPtr(bytesread), null);
fs.Close();
}
此代码抛出AccessViolationException。 但是,以下代码不会:
[DllImport("kernel32.dll", SetLastError = true)]
static extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead,
ref int numBytesRead, System.Threading.NativeOverlapped* overlapped);
..<cut>..
byte[,,] mb = new byte[1024,1024,1024];
fixed(byte * fb = mb)
{
FileStream fs = new FileStream(@"E:\SHARED\TEMP", FileMode.Open);
int bytesread = 0;
ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), ref bytesread, null);
fs.Close();
}
不同之处在于我将numBytesRead声明为ref int而不是IntPtr。
然而,在我找到“如何将IntPtr转换为int”这一问题的答案的地方,它就像:
int x = 0;
IntPtr ptrtox = new IntPtr(x)
那么,我做错了什么?为什么要访问违规行为?
答案 0 :(得分:2)
您获得访问冲突的原因是因为新的IntPtr(x)创建了一个指针,其地址是x的内容。所以你在x = 0时创建了一个NULL指针。
IntPtr构造函数未获取其参数的地址。它不等同于&amp; C / C ++中的运算符。
你想使用ref参数读取字节;这是正确的方法。此外,您总是希望使用GCHandle来获取托管对象的地址,因此在您的mb阵列上使用它,而不是修复。只是不要长时间保持手柄,不要忘记释放它。
-reilly。
答案 1 :(得分:0)
我认为访问冲突是因为管理了bytesread,因此GC可能会移动它,使您传递的指针无效。
以下是否有效?
int bytesread = 0;
var pin = GCHandle.Alloc(bytesread, GCHandleType.Pinned)
ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), pin.AddrOfPinnedObject(), null);
[编辑] 我忘记了下一行:
pin.Free();
[双重编辑] 噢亲爱的!我完全得到了错误的结束。我所说的更多地适用于以安全代码处理堆中的托管数据。
@plinth完全正确,代码:
int x = 0;
IntPtr ptrtox = new IntPtr(x)
创建一个值为x的指针,而不是指向x。在您的原始代码中,只需传递:
new IntPtr(&bytesread)
或
(IntPtr)(&bytesread)
答案 2 :(得分:0)
这很容易。看看你正在做的这件小事:
new IntPtr(bytesread)
这不符合你的想法。您认为它会生成一个指向您的变量bytesread的新指针。它没有。它生成一个指向带有值的bytesread的地址的指针,该值为0.非托管代码读取,然后尝试将数字写入空指针指向的内存中,该指针失败。
另一个版本有效,因为参数声明为ref int
,这将使编组器传递一个指向bytesread而不是值的实际指针。
答案 3 :(得分:0)
如果您处于不安全的上下文中,您可以获得指向blittable类型的指针,例如int,就像在C或C ++中一样。在你的情况下&amp; bytesread。也就是说,对于简单的指针参数,你应该总是使用ref或out关键字。