C#中的FileStream和FileSystemWatcher,奇怪的问题“进程无法访问文件”

时间:2014-02-12 20:48:43

标签: c# filestream filesystemwatcher

我有这个复杂的代码库,它正在侦听某个文件夹上的FileCreated事件。当文件被创建(其中还包括将文件移动到该文件夹​​)时,我想读取该文件并对其执行某些操作。它适用于第一个文件,但在所有其他尝试之后抛出异常。在调试模式下(使用VisualStudio),将抛出错误,但如果我只是单击"继续" ..然后它将起作用(没有错误)。

我发布了简化代码,证明了这个问题。

例如,您启动应用程序,单击"开始"按钮,然后"创建一个新的文本文件"

输出结果为:

Working

如果您以完全相同的方式创建2ed文件,则输出为:

Broken: The process cannot access the file 'C:\TestFolder\New Text Document (2).txt' because it is being used by another process.
Working, after breaking

查看我的代码后,您会看到上面的打印输出意味着首先有一个"无法访问该文件"抛出异常,但在catch语句中执行相同的调用突然有效。

这对我来说没有任何意义,因为该文件显然没有被其他任何东西使用(我刚刚创建它)..无论如何它会在一秒后工作....

以下是我的代码

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" >
    <StackPanel>
        <Button Click="Button_Click"  Content="Start"/>
    </StackPanel>
</Window>

代码背后:

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;


namespace WpfApplication1
{ 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            test();
        }


        String Folder = @"C:\TestFolder";

        private void test()
        {
            FileSystemWatcher watch = new FileSystemWatcher(Folder);
            watch.Created += new FileSystemEventHandler(FileCreated);
            watch.EnableRaisingEvents = true;

            Process.Start(Folder);
        }

        private void FileCreated(object sender, FileSystemEventArgs fsEvent)
        {

            if (File.Exists(fsEvent.FullPath))
            {

                // Thread.Sleep(1000);// Sleeping for 1 second seems to prevent the error from happening...?
                // If i am debugging, and pause on exceptions... then it also suddenly works (similar to the Sleep above)
                try
                {

                    FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open); 
                    Console.WriteLine("Working");
                    fs.Close();
                }
                catch (IOException ex)
                {
                    Console.WriteLine("Broken: " + ex.Message);
                    try
                    {                        
                        FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
                        Console.WriteLine("Working, after breaking");
                        fs.Close();

                    }
                    catch(IOException ex2)
                    {                        
                        FileStream fs = new FileStream(fsEvent.FullPath, FileMode.Open);
                        Console.WriteLine("really broken: " + ex2.Message);
                        fs.Close();
                    }
                }


            }
        }
    }
}

4 个答案:

答案 0 :(得分:8)

我已经看到了自.NET 1.0以来你所描述的行为,并且从未费心去发现它为什么会发生。在调用close和dispose之后,OS或.NET有时(?)会在短时间内锁定文件。

我做了一个解决方法 - 或者如果你喜欢黑客 - 这对我们来说已经证明非常强大。我们每天在服务器场中处理数百万个文件,并且filewatchers检测到的所有文件在传递给进一步处理之前都会通过此方法。

它的作用是对文件进行独占锁定。如果失败,它将可选择等待最多10秒钟,以便在放弃之前关闭文件。

    public static bool IsFileClosed(string filepath, bool wait)
    {
        bool        fileClosed = false;
        int         retries = 20;
        const int   delay = 500; // Max time spent here = retries*delay milliseconds

        if (!File.Exists(filepath))
            return false;

        do
        {
            try 
            {
                // Attempts to open then close the file in RW mode, denying other users to place any locks.
                FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                fs.Close();
                fileClosed = true; // success
            }
            catch (IOException) {}

            if (!wait) break;

            retries --;

            if (!fileClosed)
                Thread.Sleep( delay );
        }
        while (!fileClosed && retries > 0);

        return fileClosed;
    }

答案 1 :(得分:3)

这里发生的最有可能的事情是FileCreated事件被引发并尝试在完全写入磁盘之前处理该文件。

有关避免此问题的技巧,请参阅Wait Until File Is Completely Written

答案 2 :(得分:1)

尝试禁用任何防病毒/反恶意软件。大多数配置为默认情况下在创建时扫描文件。

答案 3 :(得分:0)

Ty to EventHorizo​​n 对于上面的代码,非常适合我的需求,并且似乎抓住了 Brendan 分享的链接中的本质。

将其稍微修改为异步返回元组(用于在 do/while 结束时获取文件锁定状态并通过 ms 以查看发生了哪种退出)。我唯一不舒服的是,我不确定为什么会这样,即使将延迟降低到 2 毫秒,我仍然无法触发 catch IOException 条件(超过一个 do 循环的证据) ) 所以我没有任何关于 IO 失败案例的直接可见性,其他人可能有更大的文件,他们可以验证这一点:

public async Task<(bool, int)> IsFileClosed(string filepath)
{
  bool fileClosed = false;
  int baseretries = 40;
  int retries = 40;
  const int delay = 250;

  if (!File.Exists(filepath))
    return (false,0);

  Task<bool> FileCheck = Task.Run(() =>
  {
    do
    {
      try
      {
        FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        fs.Close();
        fileClosed = true;
      }
      catch (IOException) { }
      retries--;

      if (!fileClosed)
        Thread.Sleep(delay);
    }
    while (!fileClosed && retries > 0);
    return fileClosed;
  });

  fileClosed = await FileCheck;
  return (fileClosed, (baseretries - retries) * delay);
}
相关问题