3

我需要以原始模式打开读取磁盘,因此为此我使用了 CreateFile API 函数。

private static FileStream OpenDisk(string drive)
{
    // Try to open hard disk drive in raw mode for reading
    SafeFileHandle safeHandle = Native.CreateFile(
        string.Format(@"\\.\{0}", drive),
        FileAccess.Read,
        FileShare.Read,
        IntPtr.Zero,
        FileMode.Open,
        FileAttributes.ReadOnly | FileAttributes.Device,
        IntPtr.Zero);

    // Check if the drive was successfully opened
    if (safeHandle.IsInvalid)
    {
        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
    }

    // Create file stream on the file for reading
    return new FileStream(safeHandle, FileAccess.Read);
}

但是当我尝试从流中读取时,出现以下错误

Handle does not support synchronous operations. The parameters to the FileStream constructor may need to be changed to indicate that the handle was opened asynchronously (that is, it was opened explicitly for overlapped I/O).

这是重现此问题的示例代码

using (FileStream stream = OpenDisk("X:"))
{
    byte[] buffer = new byte[1000];
    while (stream.Read(buffer, 0, 1000) > 0) { }
}

我真的不明白它想从我这里得到什么?当我使用更大的缓冲区(例如 4096)时,它可以工作。当我说它有效时,我的意思是它确实有效(总是),它工作了一段时间,直到我改变了缓冲区大小。我猜它在内部做了某种异步缓冲,当我指定更大的缓冲区然后默认缓冲大小时,它只是不使用,但是如何摆脱这个?

谢谢

更新

当我尝试使用 BufferedStream 读取它时,我遇到了同样的问题

using (BufferedStream stream = new BufferedStream(OpenDisk("X:"), 4096))
{
    byte[] buffer = new byte[1000];
    while (stream.Read(buffer, 0, 1000) > 0) { }
}

我是否理解 BufferedStream 的错误用途?它不应该读取和缓存指定大小的块吗?

4

1 回答 1

4

简而言之,来自卷的读取是非缓冲的,并且必须是整个扇区。4096 的缓冲区大小实现了这一点。该错误消息具有误导性,因为异步 I/O 和非缓冲往往同时出现。一些细节:

CreateFile 文档

卷句柄可以根据特定文件系统的判断以非缓存方式打开,即使在 CreateFile 中未指定非缓存选项也是如此。您应该假设所有 Microsoft 文件系统都以非缓存方式打开卷句柄。对文件的非缓存 I/O 的限制也适用于卷。

文件缓冲文档

  • 文件访问大小,包括 OVERLAPPED 结构中的可选文件偏移量(如果指定),必须是卷扇区大小的整数倍的字节数。例如,如果扇区大小为 512 字节,则应用程序可以请求读取和写入 512、1,024、1,536 或 2,048 字节,但不能请求 335、981 或 7,171 字节。
  • 读写操作的文件访问缓冲区地址应该是物理扇区对齐的,这意味着在内存中的地址上对齐,该地址是卷物理扇区大小的整数倍。根据磁盘的不同,可能不会强制执行此要求。
于 2012-09-26T18:47:26.033 回答