C#获取正确的IntPtr指向已经声明的变量

时间:2012-12-13 13:12:36

标签: c# pinvoke intptr

我正在尝试直接向/从文件写/读多字节数组,并建议使用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 个答案:

答案 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关键字。