【问题标题】:Low level file access with .NET.NET 的低级文件访问
【发布时间】:2015-05-08 03:51:25
【问题描述】:

.NET 框架中是否有任何类提供对 \.\G: - 样式路径的访问。 (即原始卷)?

我们目前使用 p/invoked ReadFile 和 WriteFile 没有任何问题,这对于同步访问并不复杂,但是添加异步读/写很乏味,因为您需要接管固定和处理 OVERLAPPED 结构和管理事件对象的生命周期等(即我们必须在 Win32 代码中做的所有繁琐的事情......)

使用任何简单的测试技术都很难证明您与 GC 的交互也是正确的。

毫无疑问,FileStream 类包含所有这些代码,以一种完全防弹和精致的方式,并利用了许多我们无法使用的内部帮助程序。不幸的是,FileStream 明确阻止您打开原始卷,因此我们无法使用它。

框架中还有什么其他东西可以帮助避免从头开始编写这种代码吗?我已经在参考源中戳了一下,但没有跳出来。

更新 - 我们已经尝试了以下建议,以避免通过自己打开设备并传入句柄来检查路径类型。当我们尝试这样做时,它会出现以下错误(请注意,此跟踪通过 FileStream 的构造函数 - 即我们根本没有任何机会与流交互): p>

System.IO.IOException: The parameter is incorrect.

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.SeekCore(Int64 offset, SeekOrigin origin)
at System.IO.FileStream..ctor(SafeFileHandle handle, FileAccess access, Int32 bufferSize, Boolean isAsync)
at OurApp.USBComms.UsbDevice..ctor(Char driveLetter) in 

作为参考,我们的 CreateFile 调用如下所示:

var deviceName = String.Format(@"\\.\{0}:", driveLetter);

var handle = SafeNativeMethods.CreateFile(deviceName,
     0x80000000 | 0x40000000,
     FileShare.ReadWrite,
     0,
     FileMode.Open,
     (uint)FileOptions.Asynchronous | 0x20000000, // Last option is       'FILE_FLAG_NO_BUFFERING'
     IntPtr.Zero);
     if (handle.IsInvalid)
     {
        throw new IOException("CreateFile Error: " + Marshal.GetLastWin32Error());
     }

Update3:事实证明(无论如何,在卷句柄上),您不能在已使用 FILE_FLAG_OVERLAPPED 打开的句柄上调用 SetFilePointer。这是有道理的,因为 SetFilePointer 在无论如何都存在任何类型的多线程访问的文件上是无用的。不幸的是,FileStream 似乎出于某种原因(仍在试图追查原因)决定在构建期间调用它,这就是导致失败的原因。

【问题讨论】:

  • 让我休息一下!这种问题现在在 SO 上是否离题?我穿过镜子了吗?
  • 我不认为这是题外话。也许亲近的选民对此感到困惑框架中还有什么其他东西可以帮助避免从头开始编写这种代码吗?
  • @WillDean 这正是 SO 注定失败的原因。这里的人现在是机器人。你不能在一个问题中说“嗨”,或者在不发布一堆代码的情况下提出一些问题,而不会被否决。悲伤,但真实:/
  • 我知道这是主题:-)。从 2008 年 8 月到现在,我已经掌握了 SO 的一般概念......
  • 也许您应该考虑通过 kernel32 使用 IOCP 进行异步调用,而不是切换到托管代码 github.com/cloudfoundry-incubator/if_warden/blob/…

标签: c# .net filesystems


【解决方案1】:

正如 Sriram Sakthivel 所说 (+1),您可以将 SafeFileHandle 传递给 FileStream 构造函数。

从您的堆栈跟踪中,我假设您尝试将流寻找到无效位置;原始磁盘/卷有关于读/写位置的特殊规则。

例如,您不能在扇区中间开始读取磁盘(您必须读取/查找每个 512 字节的块)。例如,尝试在偏移量 0 处读取,看看它是否工作得更好。

【讨论】:

  • 我假设SetFilePointer 中指出的规则适用...
  • 您对对齐问题可能是正确的 - 我们在现有代码中对此进行了适当的注意。问题是 没有试图在任何地方寻找它 - 该调用来自 FileStream 类的 ctor。
  • @WillDean 那是构造函数试图为你寻找流的来源,但我不知道为什么它是这样实现的,因为如果句柄是异步的,它就没有意义(除非我遗漏了什么)。
  • @ken2k - 是的 - 遗憾的是,如果它是 FILE_TYPE_DISK,它会寻求归零,我猜它是音量句柄。我 100% 同意你们的观点,即异步句柄毫无意义,但无论如何。我想现在 .NET 核心是开源的,我可以在 GitHub 上找到它并提交 PR...
【解决方案2】:

我相信您可以使用 constructor of FileStream 将预先打开的 FileHandle 作为 SafeFileHandle 实例传递。有了它,您已经管理了 FileStream 实例,您可以使用它来发出异步 I/O 操作。

public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync);

此外,当您计划进行异步 I/O 时,不要忘记将 isAsnc 标志设置为 true。否则you'll not get all the benefits of async I/O.

【讨论】:

  • 是的,我们确实尝试过这种颠覆性的方法,但是当 FileStream 尝试对该句柄执行不适当的操作时,它会在其中某处遇到“错误参数”错误。
  • @WillDean 你能告诉我更多关于它在哪里引发这个错误的细节吗?也许我们做错了什么?
  • 我已经将堆栈跟踪添加到 OP 中,当我们传入一个句柄时它会去哪里 - 我要去仔细检查我们传入的句柄的质量, 如果你认为这应该可行
  • @WillDean 我之前没试过。但我相信这应该可行,我认为这没有理由失败。您可以更新代码以显示如何获取 FileHandle 吗?这样我就可以尝试在本地重现问题?
  • 我已将 CreateFile 代码放入 OP - 我将在 Windbg 中捕获 Win32 SetFilePointer 调用并查看它被要求寻找的位置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多