当使用C#/.Net时,只读属性如何影响目录?

3

我发现我可以在只读目录中写入文件(例如:复制文件到只读目录中),也就是说,一个目录的...Attributes = FileAttributes.ReadOnly属性可以被修改。我甚至可以修改它的名称,唯一无法完成的操作就是删除该目录。那么,ReadOnly属性真的只防止删除吗?

编辑:

以下是代码(该目录为空):

(new DirectoryInfo(path)).Attributes = FileAttributes.ReadOnly;
Directory.Delete(path);

它抛出一个 Access to the path 'c:\... is denied. 异常。

但是将 ReadOnly 改为 Normal 后,它可以正常工作。

那么 ReadOnly 到底防止了什么,又没有防止什么?(当然是从程序上来看。 不是通过 Windows Explorer。)

编辑2:

我已经收到与文档链接的答案,指出在目录上不支持 ReadOnly,并且很可能是 .Net 导致删除失败。 所以我会重新表达问题:“在使用C#/.Net时,Read-Only 如何影响目录?”


请注意,在不同版本的Windows上,文件夹上的readonly标志行为会有所不同。虽然在XP及之前的版本中,它的行为非常类似于文件上的readonly,但在Vista中,它发生了重大变化。请谷歌“vista directory readonly”获取更多信息。 - Lucero
@Lucero 谢谢。我正在使用Windows 7。 - ispiro
@Lucero 是的。我相信修改取决于Windows Explorer。我尝试使用一个应用程序删除只读目录,但这个应用程序在我的操作后停止工作。祝你有美好的一天 :) - Picrofo Software
3个回答

1
如Damien_The_Unbeliever所提到的,如果我们查看FILE_ATTRIBUTE_READONLY的Win32 API,它会提到:
该属性在目录上不起作用。
另请参阅:http://go.microsoft.com/fwlink/p/?linkid=125896 因此,似乎您可以使用win32或Explorer简单地删除这些目录。然而,.NET似乎在删除它们之前检查目录上的标志。例如,在Directory.Delete上使用DotPeek或Reflector即可看到此情况。这就是导致“访问被拒绝”错误的原因。
编辑:
我对此进行了更详细的研究,似乎不是.NET引发了访问被拒绝的错误。请考虑以下测试代码:
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace ReadOnlyDirTest
{
   class Program
   {
      [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
      extern static bool RemoveDirectory(string path);

      static String CreateTempDir()
      {
         String tempDir;
         do
         {
            tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
         } while (Directory.Exists(tempDir));

         Directory.CreateDirectory(tempDir);
         return tempDir;
      }

      static void Main(string[] args)
      {
         var tempDir = CreateTempDir();

         // Set readonly.
         new DirectoryInfo(tempDir).Attributes |= FileAttributes.ReadOnly;

         try
         {
            Directory.Delete(tempDir);
         }
         catch (Exception e)
         {
            Console.WriteLine("Directory.Delete: " + e.Message);
         }

         if (!Directory.Exists(tempDir))
            Console.WriteLine("Directory.Delete deleted directory");

         try
         {
            if (!RemoveDirectory(tempDir))
               Console.WriteLine("RemoveDirectory Win32 error: " + Marshal.GetLastWin32Error().ToString());
         }
         catch (Exception e)
         {
            Console.WriteLine("RemoveDirectory: " + e.Message);
         }

         if (!Directory.Exists(tempDir))
            Console.WriteLine("RemoveDirectory deleted directory");

         // Try again without readonly, for both.
         tempDir = CreateTempDir();
         Directory.Delete(tempDir);
         Console.WriteLine("Directory.Delete: removed normal directory");

         tempDir = CreateTempDir();
         if (!RemoveDirectory(tempDir))
            Console.WriteLine("RemoveDirectory: could not remove directory, error is " + Marshal.GetLastWin32Error().ToString());
         else
            Console.WriteLine("RemoveDirectory: removed normal directory");

         Console.ReadLine();
      }
   }
}

在我的机器上(win 7)运行此命令,我得到以下输出:

    Directory.Delete: Access to the path 'C:\...\Local\Temp\a4udkkax.jcy' is denied.
    RemoveDirectory Win32 error: 5
    Directory.Delete: removed normal directory
    RemoveDirectory: removed normal directory

我们可以看到错误代码为5,根据http://msdn.microsoft.com/en-gb/library/windows/desktop/ms681382(v=vs.85).aspx的解释,这是一个访问被拒绝的错误。

我只能假设资源管理器在删除目录之前取消了只读属性,这当然很容易实现。命令rmdir也可以删除标记为只读的目录。

正如文档所建议的那样,只读标志不应该被用于目录(即使在Win 7中似乎是这样),因此我不会依赖这种行为。换句话说,我不会依赖只读属性来防止任何操作。


你的链接指的是Windows资源管理器。但是,.net和系统之间的区别可能是解决这个矛盾的答案。 - ispiro
目前这是我们能得到的最接近答案的信息 - 文档似乎有误,因此很难知道还会受到哪些影响。 - ispiro

1

不是这样的。剥离足够的层,你会发现用于更改目录属性的函数是{{link1:SetFileAttributes}}:

设置文件或目录的属性。

请注意:

FILE_ATTRIBUTE_READONLY 1 (0x1) 只读文件。应用程序可以读取该文件,但无法写入或删除它。此属性在目录上不起作用。有关详细信息,请参阅“无法查看或更改 Windows Server 2003、Windows XP 或 Windows Vista 中文件夹的只读或系统属性”。

(我强调)


从我发布的代码中(尝试一下),你可以看到它实际上确实遵守了这个。 - ispiro
@ispiro - 我已经链接到官方文档。它被记录为没有影响。一个特定的程序,工作在Win32 API上面几层的选择是否抛出错误与文件系统级别的有效性无关。 - Damien_The_Unbeliever
是的,你说得对。也许Marcus(在这里的一个答案中)说得对,是.Net导致了删除失败。 - ispiro

1
文件系统中目录条目上的只读属性有限的用处。大多数用户都希望目录中的所有文件也变为只读。但这不是NTFS的工作方式,属性仅适用于文件系统对象本身,并且不像安全属性那样“继承”。
请注意,资源管理器在其用户界面中修改了属性的工作方式,当您打开它时,它会使所有文件变为只读,而不是在目录本身上设置属性。
但是,是的,它确实起作用,它可以防止修改目录对象本身。因此,如果您在代码中打开它,则可以防止删除目录。就像对文件一样。

谢谢。不过我在想,如果有其他操作需要防止,那么程序是否会在必要时移除只读属性。 - ispiro
我想不出其他的,你仍然可以重命名它,更改其他属性,修改时间戳。就像我说的,这并没有太大的用处。 - Hans Passant

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