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)
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().