0

我正在尝试将多字节数组直接写入/读取文件,并建议使用 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)

那么,我做错了什么?为什么访问冲突?

4

4 回答 4

2

您获得访问冲突的原因是因为 new IntPtr(x) 创建了一个指针,其地址是 x 的内容。所以你在 x = 0 时创建了一个 NULL 指针。

IntPtr 构造函数没有获得其参数的地址。它不等同于 C/C++ 中的 & 运算符。

您想使用 ref 参数读取字节;这是正确的方法。此外,您总是希望使用 GCHandle 来获取托管对象的地址,因此请在您的 mb 数组上使用它,而不是固定的。只是不要长时间保持手柄,也不要忘记释放它。

-赖利。

于 2012-12-13T13:38:28.090 回答
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)
于 2012-12-13T13:27:33.547 回答
0

这很容易。看看你正在做的这件小事:

new IntPtr(bytesread)

这并不像你认为的那样。您认为它创建了一个指向变量 bytesread 的新指针。它没有。它创建了一个指针,该指针指向一个值为bytesread 的地址,即 0。非托管代码读取,然后尝试将一个数字写入空指针指向的内存中,但失败了。

另一个版本有效,因为参数被声明为ref int它将使编组器将实际指针传递给 bytesread 而不是值。

于 2012-12-13T13:41:32.527 回答
0

如果你在一个不安全的上下文中,你可以获得一个指向 blittable 类型的指针,比如 int,就像在 C 或 C++ 中一样。在你的情况下 &bytesread。也就是说,对于简单的指针参数,您应该始终使用 ref 或 out 关键字。

于 2012-12-13T20:39:42.717 回答