在Windows中执行文件时,File.Open读取权限被拒绝

5

我在Windows上执行文件时遇到了文件权限问题,根据一个论坛提示[1]解决了这个问题,但是我不知道为什么会这样。希望你们能帮忙解释一下。

我通过执行文件(读取控制台输出)来检查文件的标语,然后使用FileStream打开同一文件进行读取:

public void fileMD5(string filename) {
  if (!File.Exists(filename)) return NT.Fail("File does not exist: " + filename);

  BinaryReader stream = new BinaryReader(File.Open(filename,
      FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
  int bufferSize = 4096;
  byte[] buffer = new byte[bufferSize];
  int readBytes;
  while ((readBytes = stream.Read(buffer, 0, bufferSize)) > 0) {
    md5Hasher.TransformBlock(buffer, 0, readBytes, buffer, 0);
  }
  stream.Close();
}

fileMD5('sample.exe');

每隔一段时间我会收到“文件正在被另一个进程使用”的消息。从维基百科上了解到,Windows 会在执行文件时设置锁定以拒绝写入权限[2],但我只是在读取。此外,当我尝试打开它时,该进程应该已经停止。
从论坛帖子上看来,添加 FileShare.ReadWrite 应该会有所帮助,并且似乎确实有效:
FileStream stream = File.Open('sample.exe', 
    FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

但是我不明白为什么会这样。这里是否存在我没有看到的竞态条件?

此外,使用FileShare.ReadWrite而不是默认值(我猜是FileShare.Read),调用File.Open似乎要快得多。

[1] http://www.xtremevbtalk.com/archive/index.php/t-192118.html

[2] http://en.wikipedia.org/wiki/File_locking#In_Microsoft_Windows


你是否正确关闭了之前的文件句柄?请提供更多代码。 - mtijn
你是在等待 sample.exe 退出吗? - Mehran
你必须关闭之前迭代的流。 - Vamsi
文件之前没有句柄,但是仍然提供了更多的代码。我确实等待sample.exe退出,是的。 - Vegar Westerlund
是的,我正在使用Win7。这有关系吗? - Vegar Westerlund
4个回答

12

当您未指定 FileShare 参数时,此选项的默认值为 FileShare.None,实际上 File 类中的代码只是执行以下操作:

public static FileStream Open(string path, FileMode mode, FileAccess access)
{
    return File.Open(path, mode, access, FileShare.None);
}

就性能而言,我只能想象指定FileShare.ReadWrite意味着Windows不需要在文件上获得锁定。

至于你所遇到的“文件正在被另一个进程使用”错误,如果您将流变量的使用包装在using块中,以便在完成后立即处置Stream,这个问题是否会消失?

using (var stream = File.Open('sample.exe', FileMode.Open, FileAccess.Read))
{
  //do something with the stream here
}

如果我在Open调用中添加FileShare.ReadWrite,问题就会消失。快速处理流并不重要,因为我首先无法创建它。问题似乎是即使文件的执行已经停止,锁仍然存在(或正在被删除),因为我打开文件进行读取。 - Vegar Westerlund
哈,解决方案的关键实际上在问题中:“我猜是FileShare.Read”。在编程中不应该猜测,正如lowds指出的那样,默认值实际上是FileShare.None。对于MD5检查,允许其他人读取是可以的,所以我应该明确说明。正确的实现方法应该是:File.Open('sample.exe', FileMode.Open, FileAccess.Read, FileShare.Read)。 - Vegar Westerlund

2

您应该关闭FileStream,然后再打开一个新的FileStream。

FileShare是需要的,当应用程序想要共享一个文件不仅限于一个应用程序或者一个应用程序同时有多个读取器或写入器的时候。

为什么?当每个人都可以同时读写时,会变得混乱。在这种情况下,最好明确设置它,以便清楚地表明它会变得混乱。 :)


我没有写手,但我知道Windows在执行文件时会设置一些权限。我只是不知道它是如何工作的。它应该允许读取访问权限,但有时候这个检查会在50次运行中失败1次。 - Vegar Westerlund
@Vegar 当你设置FileShare.None时,无论是读取还是写入文件,它都会始终锁定该文件。 - Skomski

1

看起来指定错误的FileShare会禁止您访问文件。如果您指定了FileShare.Read,但某个其他应用程序当前具有对该文件的写访问权限,则无法访问该文件,因为您的FileShare.Read当前不可满足。 FileShare.ReadWrite不那么严格,因为它更容易满足。来源:http://blogs.msdn.com/b/oldnewthing/archive/2004/05/11/129759.aspx


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接