FileStream and a FileSystemWatcher in C#, Weird Issue “process cannot access the file”

独自空忆成欢 提交于 2019-12-04 01:58:36

问题


I have this complicated code-base that is listening for FileCreated events on a certain folder. When the file is Created (which also includes moving a file to that folder), I want to read in that file and do something with it. It works for the first file, but throws an exception after for all other attempts. In Debug-mode (With VisualStudio) the error would be thrown, but if i just click "continue".. it would then work (without an error).

I have posted simplified code, which demonstrates the issue.

For example, you start the application, click the "start" button, and then "create a new text file"

The output is:

Working

If you then create a second file in the exact same manner, the output is:

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

After looking at my code, you will see that the above set of print-outs implies that first there was an "cannot access the file" exception thrown, but doing the same call in the catch-statement suddenly works.

This makes no sense to me, since the file is clearly not being used by anything else (i just created it).. and it works a second later anyhow....

Below is my code

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>

Code Behind:

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();
                    }
                }


            }
        }
    }
}

回答1:


I've seen the behavior you describe since .NET 1.0 and never bothered finding out why it happens. It seems like the OS or .NET sometimes(?) keep a lock on the file for a short time after you called close and dispose.

I made a workaround - or hack if you like - that has proven to be very robust for us. We're processing millions of files daily in a server farm, and all files detected by filewatchers pass through this method before they are handed over for further processing.

What it does is to place an exclusive lock on the file. If it fails it will optionally wait up to 10 seconds for the file to be closed before giving up.

    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;
    }



回答2:


Most likely what is happening here is that the FileCreated event is being raised and tries to process the file before is has been completely written to disk.

See Wait Until File Is Completely Written for a technique to avoid this problem.




回答3:


Try disabling any antivirus/anti-malware software. Most are configured to scan files on create by default.



来源:https://stackoverflow.com/questions/21739242/filestream-and-a-filesystemwatcher-in-c-weird-issue-process-cannot-access-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!