【问题标题】:.NET File Access Violation.NET 文件访问冲突
【发布时间】:2014-07-23 16:03:14
【问题描述】:

我觉得发布这个有点愚蠢,但这似乎是一个真正的问题,我已经把它做得足够简单,以证明它不应该失败。作为我工作的一部分,我负责维护构建系统,这些系统将文件置于版本控制之下,并将它们复制到其他位置。听起来很简单,但在尝试复制我认为已经设置为“正常”的文件时,我经常遇到文件访问冲突。

下面的代码示例只是创建了一组测试文件,将它们设为只读,然后将它们复制到另一个文件夹。如果目标文件夹中已经存在文件,则清除RO属性,以免文件复制失败。

代码工作到一定程度,但在看似随机的点尝试文件复制时会引发异常。代码都是单线程的,所以除非 .NET 在后台做一些会导致属性设置延迟的事情,否则我无法真正解释这个问题。

如果有人能解释为什么会发生这种情况,我会很感兴趣。除非我确实做错了什么,否则我不会寻找解决方案,因为我已经处理了这个问题,我只是好奇,因为似乎没有其他人报告与此相关的任何事情。

经过几次迭代,我得到了类似的东西:

“System.UnauthorizedAccessException”类型的第一次机会异常发生在 mscorlib.dll 中 附加信息:对路径“C:\TempFolderB\TEMPFILENAME8903.txt”的访问被拒绝。

另一个事实,如果您在文件复制之前获得文件属性,则结果状态表明文件属性确实正常,但检查本地文件显示为只读。

    /// <summary>
    /// Test copying multiple files from one folder to another while resetting RO attr
    /// </summary>
    static void MultiFileCopyTest()
    {
        /// Temp folders for our test files
        string folderA = @"C:\TempFolderA";
        string folderB = @"C:\TempFolderB";

        /// Number of files to create
        const int fileCount = 10000;

        /// If the test folders do not exist populate them with some test files
        if (System.IO.Directory.Exists(folderA) == false)
        {
            const int bufferSize = 32768;

            System.IO.Directory.CreateDirectory(folderA);
            System.IO.Directory.CreateDirectory(folderB);

            byte[] tempBuffer = new byte[bufferSize];

            /// Create a bunch of files and make them all Read Only
            for (int i = 0; i < fileCount; i++)
            {
                string filename = folderA + "\\" + "TEMPFILENAME" + i.ToString() + ".txt";

                if (System.IO.File.Exists(filename) == false)
                {
                    System.IO.FileStream str = System.IO.File.Create(filename);
                    str.Write(tempBuffer, 0, bufferSize);
                    str.Close();
                }

                /// Ensure files are Read Only
                System.IO.File.SetAttributes(filename, System.IO.FileAttributes.ReadOnly);
            }
        }

        /// Number of iterations around the folders
        const int maxIterations = 100;

        for (int idx = 0; idx < maxIterations; idx++)
        {
            Console.WriteLine("Iteration {0}", idx);

            /// Loop for copying all files after resetting the RO attribute
            for (int i = 0; i < fileCount; i++)
            {
                string filenameA = folderA + "\\" + "TEMPFILENAME" + i.ToString() + ".txt";
                string filenameB = folderB + "\\" + "TEMPFILENAME" + i.ToString() + ".txt";

                try
                {

                    if (System.IO.File.Exists(filenameB) == true)
                    {
                        System.IO.File.SetAttributes(filenameB, System.IO.FileAttributes.Normal);
                    }

                    System.IO.File.Copy(filenameA, filenameB, true);
                }
                catch (System.UnauthorizedAccessException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
    }

【问题讨论】:

  • 查看文件夹中的文件数?根据您的文件系统类型,存在限制:superuser.com/questions/16207/…
  • 对于任意数量的文件,无论大小,都会发现同样的问题,并且随机地,前几次迭代通常都很好。我的测试显示每 1,000,000 个文件大约有 23 次失败。
  • 病毒扫描程序正在运行?
  • 尝试禁用所有 AV / 备份软件,也在多台 PC 上尝试过,都显示完全相同的结果。
  • 我也注意到了这一点,我肯定很想听听一些想法。我最终在一些项目中使用了本机 CopyFile,这并没有给我带来任何问题。

标签: c# .net


【解决方案1】:

(这不是一个完整的答案,但我还没有足够的声誉来发布 cmets...)

我不认为你做错了什么,当我运行你的测试代码时,我每次都能重现问题。我从来没有超过 10 次迭代而不发生错误。

我做了进一步的测试,这可能会对这个问题有所了解:

我将 TempFolderA 中的所有文件都设置为隐藏。

然后我确保 TempFolderB 中的所有文件都没有被隐藏。

我在Console.WriteLine(ex.Message)上设置了一个断点

我运行了代码,如果它通过了第 1 次迭代,我就停下来,重置隐藏属性并再次运行。

经过几次尝试,我在第一次迭代中失败了,所以我在 TempFolderB 上打开了 Windows 资源管理器并向下滚动到有问题的文件。

文件大小为 0 字节,但设置了 RHA 属性。

不幸的是,我不知道这是为什么。 Process Monitor 没有显示任何其他可能相关的活动。

【讨论】:

  • 这些结果与我自己的匹配,当它检查有问题的文件失败时,它显示为零字节并且是只读的,但根据文件状态,如果在复制操作之前立即读取返回正常.我猜这是 .NET 或更低级别中的某个错误,我只是很惊讶如此明显的事情没有得到解决,因为它肯定让我有些头疼。
【解决方案2】:

嗯,在System.IO.File.SetAttributes(string path, System.IO.FileAttributes attributes) 方法的文档中,我发现了以下内容:

例外情况: System.UnauthorizedException:
   path 指定了一个只读文件。
   -或- 当前平台不支持此操作。
   -或-调用者没有所需的权限。

所以,如果我不得不猜测,目标中的文件(例如filenameB)实际上已经存在。它被标记为只读,因此按照上面的文档抛出了异常。

相反,您需要做的是通过反向位掩码删除只读属性:

if (FileExists(filenameB))
{
    // Remove the read-only attribute
    FileAttributes attributes = File.GetAttributes(filenameB);
    attributes &= ~FileAttributes.ReadOnly;
    File.SetAttributes(filenameB, attributes);

    // You can't OR the Normal attribute with other attributes--see MSDN.
    File.SetAttributes(filenameB, FileAttributes.Normal);
}

公平地说,SetAttributes 方法的文档并不清楚一旦文件被标记为Readonly,如何设置文件属性。当然,有一个例子(使用Hidden 属性),但他们没有明确说您需要使用反转位掩码来删除HiddenReadonly 属性。人们可以很容易地假设这就是他们选择“取消设置”属性的方式。从文档中也不清楚会发生什么,例如,如果您这样标记文件:

File.SetAttributes(pathToFile, FileAttributes.Normal);
File.SetAttributes(pathToFile, FileAttributes.Archived);

这会导致文件首先设置Normal 属性,然后 Archived,还是导致文件设置Normal,然后_additionallyhavingArchived ` 设置,导致正常但存档的文件?我相信是前者,而不是后者,这取决于如何使用倒置位掩码从文件中“删除”属性。

如果有人发现任何相反的地方,请发表评论,我会相应地更新我的答案。

HTH。

【讨论】:

  • 感谢您的解释。不幸的是,它对问题没有任何影响,如果出现这样的问题,它将影响 1,000,000 个文件中的 23 个以上。文件属性中位域的设置和清除非常清晰直接。设置为正常只会清除所有其他位。您的示例需要更改为: attributes &= ~FileAttributes.ReadOnly;我最初设置了代码以屏蔽 RO 位,但将其更改为使用 Normal 以使示例更易于理解/阅读。
  • 设置为正常不会清除所有其他位。它将“正常”位添加到现有位。而且,由于该文件将ReadOnly 作为其位的一部分,因此该文件是只读的,因此您无法将Normal 位添加到文件安全性中。根据引用的 MSDN 文档,您首先需要通过反向位掩码删除 ReadOnly 位,然后设置 Normal 位。
  • @mindeater 关于&amp;=|=,你是对的。固定的。谢谢。
  • @mindeater 也许是设置ReadOnly 位的方式。在 MSDN 文档中,因此设置了 Hidden 标志(并且 ReadOnly 的工作方式相同):File.SetAttributes(filenameB, File.GetAttributes(filenameB) | FileAttributes.ReadOnly);。我相应地更新了我的代码示例。
  • 我不确定您是根据什么来提供这些信息的,请称我为老派,但我刚刚验证了将属性设置为 Normal 确实会删除所有其他属性。它只是必须,属性只是使用位值。测试代码: System.IO.File.SetAttributes(testFilename, System.IO.FileAttributes.ReadOnly | System.IO.FileAttributes.Hidden | System.IO.FileAttributes.Offline); System.IO.File.SetAttributes(testFilename, System.IO.FileAttributes.Normal);
【解决方案3】:

这可能是因为用户没有足够的权限访问文件系统,

解决方法:

1,尝试以管理模式运行应用程序

2、尝试在管理模式下运行visual studio(如果你使用的是调试器)

【讨论】:

  • 也许我没有说清楚,错误是随机出现的,通常是在示例代码中的 10,000 个文件成功迭代多次之后,并且在我测试过的所有电脑上都失败了。这对我来说不是本地问题,它似乎是 .NET(或更低级别)中的错误,如果是权限问题,它将在第一个文件上失败,而不是在文件访问 40,456 上失败。
猜你喜欢
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 1970-01-01
  • 2017-05-06
  • 2013-02-26
  • 2015-04-23
相关资源
最近更新 更多