-1

我正在尝试使 kernel32 的 ReadFile 工作,因为本机 c# 替代品很慢,因为...

以下代码运行了几次,然后突然只给出“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”

我认为重新启动服务器会使其工作几次,然后再出现相同的错误,但似乎我错了,无论我现在做什么都会发生..

这是我的代码:

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;
}

我认为这就是所有相关的代码。

我猜这与 CreateFile 和 ReadFile 签名不完全兼容(IntPtr 与 int)、IntPtr 的 Marshal.PtrToStructure 没有指向它应该指向的位置、或者内存没有被释放或什么有关?不过,重启后它对几次尝试都不起作用的事实让我感到困惑。

有人发现任何明显的东西或有什么我可以研究的建议吗?

谢谢

编辑:您可能会注意到,这是一种不同方法的混搭,我不再使用固定缓冲区,因为我努力让缓冲区的内容像我想要的那样读取。

Edit2:堆栈跟踪说这是问题所在:

at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Type structureType)
4

2 回答 2

3

您的 pinvoke 声明不好,请从pinvoke.net获取好的声明

ReadFile() 的第一个参数是一个句柄,即您从 CreateFile() 获得的句柄。它是 IntPtr。您通过尝试将 IntPtr 转换为 int 给自己挖了一个洞, Marshal.PtrToStructure() 调用不正确。并且几乎总是会用 AVE 炸弹,句柄不是指针。

修复 [DllImport] 声明并直接使用从 CreateFile() 获得的 IntPtr。并且不要忘记 System.IO.FileStream 是这些 winapi 函数的 .NET 包装器,如果您需要打开设备而不是文件的句柄,您只需要 pinvoke CreateFile()。然后,您仍然使用接受句柄的 FileStream 构造函数并使用其 Read() 方法调用 ReadFile()。

于 2013-10-07T14:58:00.480 回答
1

这段代码

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

不做你认为它做的事。此代码尝试将pHandleRead其视为指向int. 但这根本不是你想要的。做你想做的代码是:

int iHandleRead = pHandleRead.ToInt32();

实际上,您只会发现自己在编写该代码,因为您的 p/invoke 声明很差。您的ReadFile声明应接收文件句柄 a​​sIntPtr而不是int. 如果这样做了,那么您根本不需要上面的代码并且会通过pHandleRead.

更一般的观点:

  • 我不明白你为什么选择在unsafe这里使用代码。这里根本不需要这样做。
  • 我还认为,如果您使它工作,此代码不太可能比托管等效代码执行得更好。
于 2013-10-08T10:30:42.663 回答