【问题标题】:Directory.Move(): Access to Path is DeniedDirectory.Move():拒绝访问路径
【发布时间】:2013-09-26 07:33:52
【问题描述】:

我正在使用 C# 在 Visual Studio 2010 中编写这个 Windows 窗体应用程序。

表单上有一个执行按钮,用户点击该按钮,程序会生成一些文件并存放在Output文件夹中(由程序使用Directory.CreateDirectory()创建)

我想创建一个存档文件夹来保存以前运行的输出文件。

在每次运行开始时,我尝试将现有的Output 文件夹移动到Archive 文件夹,然后创建一个新的Output 文件夹。下面是我运行的移动目录的函数。

static void moveToArchive()
{
    if (!Directory.Exists("Archive")) Directory.CreateDirectory("Archive");
    string timestamp = DateTime.Now.ToString("yyyyMMddHHmms");
    try
    {
        Directory.Move("Output", "Archive\\" + timestamp);
    }
    catch(Exception e)
    {
        Console.WriteLine("Can not move folder: " + e.Message);
    }
}

我遇到的问题让我很困惑......

有时我可以成功地将输出文件夹移动到存档,但有时会失败。

我从捕获异常中得到的错误消息是Access to path 'Output' is denied.

我检查了Output 文件夹中的所有文件都没有被使用。我不明白访问是如何被拒绝有时而不是所有时间的。

有人可以向我解释并告诉我如何解决问题吗?

--编辑--

在HansPassant注释之后,我稍微修改了函数以获取当前目录并使用完整路径。但是,我仍然遇到同样的问题。

函数现在看起来像这样:

static void moveToArchive()
{
    string currentDir = Environment.CurrentDirectory;
    Console.WriteLine("Current Directory = " + currentDir);
    if (!Directory.Exists(currentDir + "\\Archive")) Directory.CreateDirectory(currentDir + "\\Archive");
    string timestamp = DateTime.Now.ToString("yyyyMMddHHmms");
    try
    {
        Directory.Move(currentDir + "\\Output", currentDir + "\\Archive\\" + timestamp);
    }
    catch(Exception e)
    {
        Console.WriteLine("Can not move folder: " + e.Message);
    }
}

我打印出当前目录,它与我期望的一样,但我仍然无法使用完整路径。 Access to path 'C:\Users\Me\Desktop\FormApp\Output' is denied.

--编辑--

感谢大家的回答和评论。

我想你们中有些人会错过这部分,所以我要再强调一点。

Directory.Move() 有时有效,有时失败。

函数成功后,就没有问题了。 Output文件夹移动到Archive

当函数失败时,我得到的异常信息是Access to path denied.

【问题讨论】:

  • 确保有“Output”文件夹且父目录中没有名为“Output”的文件(无文件扩展名)
  • @jgauffin 输出文件夹确实存在,并且其中没有任何文件被命名为输出。当 moveToArchive() 失败时,新生成的输出文件会在 Outuput 文件夹中创建,覆盖之前运行生成的文件。
  • 此代码严重依赖于正确设置的 Environment.CurrentDirectory。总是出错,这是最糟糕的全局变量。始终使用完整路径名,例如 c:\foo\bar\baz。而且从不依赖于能够写入 c:\program 文件,UAC 阻止了这一点。
  • 您是否在资源管理器窗口或命令提示符窗口中打开了该文件夹?
  • 我猜生成文件的代码没有正确关闭它们。有时它可能会起作用,因为 GC 已经收集并完成了悬空文件句柄。在这种情况下,可能对写入文件的代码感兴趣。

标签: c# exception-handling directory access-denied


【解决方案1】:

感谢大家的回复和帮助。我已经弄清楚是什么问题了。

这是因为有一个文件没有完全关闭。

我正在检查生成的文件,但错过了程序正在读取的文件。

所有生成的文件都已完全关闭。这是我使用StreamReader 打开但没有关闭的一个文件。我修改了代码,现在没有问题,所以我认为这就是问题所在。

感谢所有 cmets 和答案,这绝对有助于我思考和解决问题。

【讨论】:

    【解决方案2】:

    http://windowsxp.mvps.org/processlock.htm

    有时,您尝试移动或删除文件或文件夹并收到访问冲突或文件正在使用 - 错误。要成功删除文件,您需要识别锁定文件的进程。您需要先退出该过程,然后删除特定文件。要知道哪个进程锁定了文件,您可以使用本文中讨论的方法之一。

    使用 Process Explorer - 从http://download.sysinternals.com/files/ProcessExplorer.zip下载

    进程资源管理器向您显示有关已打开或加载哪些句柄和 DLL 进程的信息。

    从 Microsoft 站点下载 Process Explorer 并运行该程序。 单击查找菜单,然后选择查找句柄或 DLL... 输入文件名(被某个进程锁定的文件的名称。) 输入搜索词组后,单击“搜索”按钮 您应该会看到正在访问该文件的应用程序列表。

    【讨论】:

      【解决方案3】:

      我最近遇到了同样的问题。使用 PE,我发现使用该特定目录的唯一进程是 explorer.exe。我用资源管理器打开了几个窗口,一个指向我要移动的一个的父目录。

      看起来,在我访问了该子文件夹然后返回(甚至返回到根级别!)之后,资源管理器仍保留该句柄,因此 C# 无法以任何方式修改它(更改标志、属性等) .).

      为了让 C# 正常运行,我不得不关闭该资源管理器窗口。

      【讨论】:

      • 我在使用 Windows 资源管理器时遇到了同样的问题。认为仅仅查看目录会导致访问被拒绝是非常荒谬的。
      【解决方案4】:
      File.SetAttributes(Application.dataPath + "/script", FileAttributes.Normal);
      Directory.Move(Application.dataPath + "/script", Application.dataPath + "/../script");
      

      这解决了我的问题。

      【讨论】:

        【解决方案5】:

        试试这个: 如果这不能解决,可能检查/更改防病毒软件,或者其他一些程序正在锁定某些文件或文件夹。

            static object moveLocker = new object();
            static void moveToArchive()
            {
                lock (moveLocker)
                {
                    System.Threading.Thread.Sleep(2000);  // Give sometime to ensure all file are closed.
        
                    //Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
                    string applicationPath = System.AppDomain.CurrentDomain.BaseDirectory;
                    string archiveBaseDirectoryPath = System.IO.Path.Combine(applicationPath, "Archive");
        
                    if (!Directory.Exists(archiveBaseDirectoryPath)) Directory.CreateDirectory(archiveBaseDirectoryPath);
        
                    String timestamp = DateTime.Now.ToString("yyyyMMddHHmms");
                    String outputDirectory = System.IO.Path.Combine(Environment.CurrentDirectory, "Output");
                    String destinationTS = System.IO.Path.Combine(archiveBaseDirectoryPath, timestamp);
                    try
                    {
                        Directory.Move(outputDirectory, destinationTS);                
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Can not move folder " + outputDirectory + " to: " + destinationTS + "\n" + ex.Message);
                    }
                }
            }
        

        【讨论】:

        • 感谢您的回答。但是,当我复制您的函数时,Output 文件夹根本不会移动到 Archive 中。
        • 我刚刚又试了一次,还是一动不动。我的功能有时有效,有时无效,我使用您的功能,但它根本不做动作。我还尝试将您的锁定方法放入我的函数中,但这也无济于事。
        • 我又变了。我认为现在它会起作用。请试着告诉我。我理解:从 CurrentDirectory 到 Application Directory/Archive/TimeStamp 。如果它不起作用,如果安全,禁用杀毒软件?请在此处粘贴输出错误消息。
        • 在另一台电脑上试试。
        • 非常感谢您的跟进和帮助。使用您的功能,我仍然无法让程序移动目录。该程序没有中断,因为该函数正在使用 try-catch。控制台上打印的消息如下。 “无法将文件夹 C:\Users\Me\Desktop\FormApp\Output 移动到:C:\Users\Me\Desktop\FormApp\Archive\201309211714”并在单独的行上“访问路径 'C:\Users\Me \Desktop\FormApp\Output' 被拒绝。”
        【解决方案6】:

        我也遇到过同样的问题,有时会失败,但并非总是如此。我以为我会将它包装在 Try Catch 块中,并向用户显示拒绝访问消息,一旦我将它包装在 Try Catch 块中,它就会停止失败。我无法解释为什么。

         If existingFile.FileName <> newFileName Then
            Dim dir As New IO.DirectoryInfo(existingFile.FilePath)
            Dim path As String = System.IO.Path.GetDirectoryName(dir.FullName)
            newFileName = path & "\" & newFileName
        
            File.SetAttributes(existingFile.FilePath, FileAttributes.Normal)
                Try
                    IO.File.Move(existingFile.FilePath, newFileName)
                Catch ex As Exception
        
                End Try
        End If
        

        【讨论】:

          【解决方案7】:

          我遇到了类似的问题。遵循特定模板时,循环重命名了许多目录。程序有时会在不同的目录上崩溃。它有助于在 Directory.Move 之前添加一个睡眠线程。我需要制造一些延迟。 但它会减慢复制过程。

          foreach (var currentFullDirPath in Directory.GetDirectories(startTargetFullDirectory, "*", SearchOption.AllDirectories))
                      {
                          var shortCurrentFolderName = new DirectoryInfo(currentFullDirPath).Name.ToLower();
                          if (shortCurrentFolderName.Contains(shortSourceDirectoryName))
                          {
                              // Add Thread.Sleep(1000);
                              Thread.Sleep(1000);
                              var newFullDirName = ...;
                              Directory.Move(currentFullDirPath, newFullDirName);
                          }
                      }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-12-01
            • 1970-01-01
            • 2021-10-04
            • 2017-09-05
            • 1970-01-01
            • 2016-11-11
            • 2011-03-29
            相关资源
            最近更新 更多