【问题标题】:Deleting files in use删除正在使用的文件
【发布时间】:2012-05-17 07:01:48
【问题描述】:

我创建了一个简单的程序来删除 C# 中的临时文件(为了好玩,不是主要项目)并且遇到了锁定文件(正在使用)的问题。您通常如何排除这些文件?作为参考,我收到了错误:

该进程无法访问文件“ExchangePerflog_8484fa31c65c7a31cfcccd43.dat”,因为它正被另一个进程使用。

代码:

static void Main(string[] args)
    {
        string folderPath = string.Empty;
        folderPath = System.Environment.GetEnvironmentVariable("temp");
        deleteFilesInDirectory(folderPath);
    }

    public static void deleteFilesInDirectory(string folderPath) 
    {

        try
        {
            var dir = new DirectoryInfo(folderPath);
            dir.Attributes = dir.Attributes & ~FileAttributes.ReadOnly;
            dir.Delete(true);
            MessageBox.Show(folderPath + " has been cleaned.");
        }
        catch (System.IO.IOException ex)
        {
            MessageBox.Show(ex.Message); 
            return;

        } 
    }     

【问题讨论】:

  • 澄清一下,您是要跳过正在使用的文件,还是要强制删除它们?
  • 为什么不只收集未删除的文件并在执行后显示它们?或者您需要等待它们访问?
  • 他说排除所以我认为他的意思是跳过
  • 这个问题已经回答了,检查一下:stackoverflow.com/questions/6077869/…
  • 还有一个名为“Unlocker”的实用程序 - emptyloop.com/unlocker 这可以释放锁(以及告诉你谁拥有它们)然后允许你删除它们。不过,更多的是用于调查而不是一般用途。

标签: c#


【解决方案1】:

使用 dknaack 的代码。这在我的情况下有效。

FileInfo file = new FileInfo("xyz.txt");
try
{
    for (int tries = 0; IsFileLocked(file) && tries < 5; tries++)
        Thread.Sleep(1000);
    file.Delete();
}
catch (IOException exception)
{
    Console.WriteLine(string.Format("File locked: {0}", exception);
}

【讨论】:

    【解决方案2】:

    您只需要调用一个方法,即WipeFile,代码如下所示。因此,您真正需要做的就是调用 WipeFile 并提供要删除的文件的完整路径,以及要覆盖它的次数。

    public void WipeFile(string filename, int timesToWrite)
    {
        try
        {
            if (File.Exists(filename))
            {
                // Set the files attributes to normal in case it's read-only.
    
                File.SetAttributes(filename, FileAttributes.Normal);
    
                // Calculate the total number of sectors in the file.
                double sectors = Math.Ceiling(new FileInfo(filename).Length/512.0);
    
                // Create a dummy-buffer the size of a sector.
    
                byte[] dummyBuffer = new byte[512];
    
                // Create a cryptographic Random Number Generator.
                // This is what I use to create the garbage data.
    
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    
                // Open a FileStream to the file.
                FileStream inputStream = new FileStream(filename, FileMode.Open);
                for (int currentPass = 0; currentPass < timesToWrite; currentPass++)
                {
                    UpdatePassInfo(currentPass + 1, timesToWrite);
    
                    // Go to the beginning of the stream
    
                    inputStream.Position = 0;
    
                    // Loop all sectors
                    for (int sectorsWritten = 0; sectorsWritten < sectors; sectorsWritten++)
                    {
                        UpdateSectorInfo(sectorsWritten + 1, (int) sectors);
    
                        // Fill the dummy-buffer with random data
    
                        rng.GetBytes(dummyBuffer);
    
                        // Write it to the stream
                        inputStream.Write(dummyBuffer, 0, dummyBuffer.Length);
                    }
                }
    
                // Truncate the file to 0 bytes.
                // This will hide the original file-length if you try to recover the file.
    
                inputStream.SetLength(0);
    
                // Close the stream.
                inputStream.Close();
    
                // As an extra precaution I change the dates of the file so the
                // original dates are hidden if you try to recover the file.
    
                DateTime dt = new DateTime(2037, 1, 1, 0, 0, 0);
                File.SetCreationTime(filename, dt);
                File.SetLastAccessTime(filename, dt);
                File.SetLastWriteTime(filename, dt);
    
                // Finally, delete the file
    
                File.Delete(filename);
    
                WipeDone();
            }
        }
        catch(Exception e)
        {
            WipeError(e);
        }
    }
    

    我添加了一些事件只是为了能够跟踪过程中发生的事情。

    • PassInfoEvent - 返回正在运行的通道和总次数 要运行的传球。
    • SectorInfoEvent - 返回正在写入的扇区以及 要写入的扇区总数。
    • WipeDoneEvent - 擦除过程完成的指示器。
    • WipeErrorEvent - 如果出现任何问题,则返回异常。

    Securely Delete a File using .NET

    【讨论】:

      【解决方案3】:

      嗯,我也遇到过类似的问题。当您在删除文件后不久尝试删除目录时,您必须强制 GC 从当前线程释放文件句柄

      public void DisposeAfterTest(string filePath)
          {
      
              if (File.Exists(filePath))
              {
                  File.Delete(filePath);
              }
      
              GC.Collect();
              GC.WaitForPendingFinalizers();
      
              if (Directory.Exists(this.TempTestFolderPath))
              {
                  Directory.Delete(this.TempTestFolderPath, true);
              }
      
          }
      

      【讨论】:

      • 这是未将所有文件访问类包装在 using 中的症状。正确处理文件访问句柄,您将不会遇到此问题。
      【解决方案4】:

      以下代码将从目录及其所有子目录中删除文件,不包括锁定文件,并获取未删除文件的列表。如果只考虑当前目录,则可以将 SearchOption 更改为 TopDirectoryOnly。

      string []files = Directory.GetFiles(dirPath,"*.*", SearchOption.AllDirectories); //this gets the files in all subdirectories as well
      List<string> lockedFiles = new List<string>();
      foreach(string file in files) 
      {    
          try    
          {        
              file.Delete();    
          }    
          catch (IOException)    
          {        
              lockedFiles.Add(file);    
          }
      }
      

      【讨论】:

      • @NineTails,他不是,它是一个数组,不是IEnumerable&lt;T&gt;
      【解决方案5】:

      试试下面的代码。 只需在文件删除前添加两行:

      GC.Collect();
      GC.WaitForPendingFinalizers();
      

      【讨论】:

      • 这对于尝试删除其他进程正在使用的文件没有任何影响。
      • 这与文件删除无关。这只是指示 .Net 框架强制垃圾收集。
      • 实际上,如果您使用 SQLite,这是一个完全有效的评论。见stackoverflow.com/questions/8511901/…
      • 哇,这么多反对票。这确实可能是您无法删除文件的原因。就我而言,我有很多线程在处理一个文件(内存映射)。在处理完我所有的内存映射线程并清理它们自己之后,我试图删除映射文件。它不会让我因为垃圾收集器在删除发生之前没有时间清理。这解决了我的问题,来自我的 +1!
      • 也为我修复了它,虽然这不是实际问题,恕我直言,许多登陆这里的人都有问题,即使在从另一个进程使用后文件也被阻止。
      【解决方案6】:

      说明

      无法删除当前正被另一个进程使用的文件。但是你可以等到文件没有被锁定。

      在while循环中检查,直到使用此方法解锁文件

      protected virtual bool IsFileLocked(FileInfo file)
      {
          FileStream stream = null;
      
          try
          {
              stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
          }
          catch (IOException)
          {
              //the file is unavailable because it is:
              //still being written to
              //or being processed by another thread
              //or does not exist (has already been processed)
              return true;
          }
          finally
          {
              if (stream != null)
                  stream.Close();
          }
      
          //file is not locked
          return false;
      }
      

      样本

      FileInfo file = new FileInfo("PathToTheFile");
      while (IsFileLocked(file))
          Thread.Sleep(1000);
      file.Delete();
      

      更新

      如果你想跳过锁定的文件,你可以这样做。

      //
      var dir = new DirectoryInfo(folderPath);
      foreach(var file in dir.GetFiles()) {
          try
          {
              file.Delete();
          }
          catch (IOException)
          {
              //file is currently locked
          }
      }
      

      【讨论】:

      • 例如,如果我在机器运行时删除所有临时文件,并保持 Outlook 处于打开状态,则在 Outlook 关闭之前不会删除 Outlook 拥有的临时文件。因此,程序将继续尝试执行,直到使用此方法关闭 Outlook。我宁愿检查它是否正在使用,如果它正在使用则跳过它。
      • 这种方法的问题是当IsFileLocked返回false时,意味着文件没有被锁定,它可能会再次被锁定一段时间通过执行验证的代码。这在 Windows 上很容易观察到。
      【解决方案7】:

      我不相信有任何方法可以提前知道文件是否正在使用。您可以尝试获取文件的排他锁;但是你只会用一个例外换另一个例外。

      如果这些是您正在打开的文件,请查看是否无法更好地关闭它们。如果它比这更复杂 - 您可以维护一个“删除列表”并继续重试删除,直到它成功(可能在另一个具有并发集合的线程上)。

      我也不相信有强制删除正在使用的文件的方法。

      【讨论】:

      • 有catch(System.IO.FileInUse)这样的东西
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-26
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      相关资源
      最近更新 更多