PInvoke ReadFile kernel32: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

StackOverflow https://stackoverflow.com/questions/19225407

Question

I'm trying to make kernel32's ReadFile work since native c# alternatives are slow as...

The following code was working a couple of times, then suddenly only gives "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

I figured a restart of the server would make it work a couple times before getting the same error, but it seems I was wrong there, it happens no matter what I do now..

Here's my code:

using System.Runtime.InteropServices;



private WinFileIO.WinFileIO _winFile;

_winFile = new WinFileIO.WinFileIO(new byte[bufferSize]);
_winFile.OpenForReading(_fileInfo.FullName);


public WinFileIO(Array Buffer)
{
    PinBuffer(Buffer);
    pHandleRead = IntPtr.Zero;
    pHandleWrite = IntPtr.Zero;
    bufferSize = Buffer.GetLength(0);
}


private void PinBuffer(Array Buffer)
{
    UnpinBuffer();
    gchBuf = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
    IntPtr pAddr = Marshal.UnsafeAddrOfPinnedArrayElement(Buffer, 0);
    // pBuffer is the pointer used for all of the I/O functions in this class.
    pBuffer = (void*)pAddr.ToPointer();
}


public void UnpinBuffer()
{
    if (gchBuf.IsAllocated)
        gchBuf.Free();
}


[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe System.IntPtr CreateFile
(
     string FileName,          // file name
     uint DesiredAccess,       // access mode
     uint ShareMode,           // share mode
     uint SecurityAttributes,  // Security Attributes
     uint CreationDisposition, // how to create
     uint FlagsAndAttributes,  // file attributes
     int hTemplateFile         // handle to template file
);


[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
private unsafe static extern bool ReadFile
(
    int hFile, 
    byte[] arraypBuffer,
    int NumberOfBytesToRead,
    ref int lpNumberOfBytesToRead,
    int* ptr
);


[System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
static extern unsafe bool SetFilePointer
(
    System.IntPtr hObject,
    int lDistanceToMove,
    ref int lpDistanceToMoveHigh,
    EMoveMethod dwMoveMethod
);


public void OpenForReading(string FileName)
{
    Close(true, false);
    pHandleRead = CreateFile(FileName, 3, 3, 0, OPEN_EXISTING, 0, 0);
    if (pHandleRead == System.IntPtr.Zero)
    {
        Win32Exception WE = new Win32Exception();
        ApplicationException AE = new ApplicationException("WinFileIO:OpenForReading - Could not open file " +
          FileName + " - " + WE.Message);
        throw AE;
    }
}


public void MoveOffset(int moveDistance, EMoveMethod moveMethod)
{
    if (pHandleRead != System.IntPtr.Zero)
    {
        int moveDistanceHigh = 0;
        SetFilePointer(pHandleRead, moveDistance, ref moveDistanceHigh, moveMethod);
    }
}


public unsafe Tuple<int, byte[]> ReadChunk(int bufferSize, int offset)
{
    int bytesRead = 0;
    byte[] bufBytes = new byte[bufferSize];
    MoveOffset(offset, EMoveMethod.Begin);
    int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));

    if (ReadFile(pointer, bufBytes, bufferSize, ref bytesRead, null))
    {
        byte[] outBytes = new byte[bytesRead];
        Array.Copy(bufBytes, outBytes, bytesRead);
        return new Tuple<int, byte[]>(bytesRead, outBytes);
    }
    return null;
}

I think that's all the relevant code.

I'm guessing it has something to do with the CreateFile and ReadFile-signatures not being fully compatible (IntPtr vs. int), the Marshal.PtrToStructure for the IntPtr not pointing where it should, or memory not being freed or something? The fact that it didn't work for a couple tries after a reboot confuses me though.

Anyone spot anything obvious or have any suggestions I can look into?

Thanks

Edit: As you might notice, this is kind of a mishmash of different approaches, I'm not using the pinned buffer anymore as I struggled to get the contents of the buffer read like I would want to.

Edit2: The stacktrace says this is the problem:

at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)
Was it helpful?

Solution

Your pinvoke declarations are bad, get good ones from pinvoke.net

The 1st argument of ReadFile() is a handle, the one you got from CreateFile(). It is IntPtr. You dug yourself a hole by trying to convert the IntPtr to int, the Marshal.PtrToStructure() call is not correct. And will almost always bomb with an AVE, a handle is not a pointer.

Fix the [DllImport] declaration and use the IntPtr you got from CreateFile() directly. And don't forget that System.IO.FileStream is the .NET wrapper for these winapi functions, you only ever need to pinvoke CreateFile() if you need to open a handle to a device instead of a file. You then still use the FileStream constructor that takes a handle and use its Read() method to call ReadFile().

OTHER TIPS

This code

int pointer = (int)Marshal.PtrToStructure(pHandleRead, typeof(int));

does not do what you think it does. This code attempts to treat pHandleRead as a pointer to int. But that's not what you want at all. The code that does what you want is:

int iHandleRead = pHandleRead.ToInt32();

In fact you only find yourself writing that code because your p/invoke declarations are poor. Your ReadFile declaration should receive the file handle as IntPtr and not as int. If it did so then you would not need the code above at all and would pass pHandleRead.

More general points:

  • I do not understand why you have chosen to use unsafe code here. Nothing at all here requires that.
  • I also think it unlikely that this code, if you make it work, will perform better than the managed equivalent.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top