Search code examples
c#pinvokeintptr

C# getting correct IntPtr pointing to already declared variable


I am trying to write/read multibyte array directly to/from file, and was suggested using PInvoke WriteFile/ReadFile.

Basically my reading code looks like this now:

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

This code throws an AccessViolationException. However, the following code does not:

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

The difference is that I declare numBytesRead to be ref int rather than IntPtr.

However, everywhere where I find an answer to a question "how to get IntPtr to an int", it goes like:

int x = 0;
IntPtr ptrtox = new IntPtr(x)

So, what am I doing wrong? Why access violation?


Solution

  • The reason you get an Access Violation is because new IntPtr(x) creates a pointer whose address is the contents of x. so you have created a NULL pointer when x = 0.

    The IntPtr constructor does not get the address of its argument. It isn't equivalent to the & operator in C/C++.

    You want to use the ref argument for bytes read; that is the proper way. Furthermore, you always want to use a GCHandle to get the address of a managed object, so use it on your mb array, not fixed. Just don't keep the handle for long, and don't forget to free it.

    -reilly.