Why am I getting a Unhandled Exception: System.IO.IOException when trying to read from a file being written to?

10,290

Solution 1

lines = File.ReadAllLines(args[1] + "\Chat.log").Length;

There's your problem. That method opens the file, reads all the lines and closes it again. It uses "normal" file share settings when opening the file, FileShare.Read. That denies write access to any other process that also has the file opened.

That cannot work here, you've already have the file opened with write access. The 2nd process cannot deny it. The IOException is the result.

You cannot use File.ReadAllLines() as-is here, you need to open a FileStream with FileShare.ReadWrite, pass it to a StreamReader and read all lines.

Beware the very troublesome race potential you've got here, there's no guarantee that the last line you'll read is a complete line. Getting only a \r and not the \n at the end of the line is a particularly tricky issue. This will strike randomly and infrequently, the hardest bugs to troubleshoot. Maybe your Flush() call fixes it, I've never been brave enough to put this to the test.

Solution 2

Allowing the second program ReadWrite access on the file would work in this case.

//lines = File.ReadAllLines(args[1] + "\\Chat.log").Length;
//Commenting the above lines as this would again open a new filestream on the chat.log
//without the proper access mode required.


    using (FileStream fsReader = new FileStream(args[1] + "\\Chat.log", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fsReader))
            {
                while (sr.ReadLine() != null)
                    lines++;
            }
        }

Solution 3

StreamReader sr = new StreamReader("f:\\watch\\input.txt");

input.txt might not be available for reading?

Also use the using statement instead of Close() in the 1st application (in case an exception is thrown).

Otherwise it is OK. The file share may require additional permissions though (can't really affect that).

I have missed one piece of code:

int newlines = File.ReadAllLines(path).Length;

use the stream with a StreamReader for that.

Solution 4

MSDN offers two ways to not obtain an exclusive hold:

A FileStream object will not have an exclusive hold on its handle when either the SafeFileHandle property is accessed to expose the handle or the FileStream object is given the SafeFileHandle property in its constructor.

The documentation implies that the inverse is true:

Opening a FileStream without setting the SafeFileHandle means the FileStream maintains an exclusive hold on the file handle (which is inline with the IO exception that is supposed to be thrown).

Share:
10,290
Hew Masters
Author by

Hew Masters

Hobbyist programmer, currently self teaching C# to do some work with memory reading/writing, file access and object manipulation.

Updated on June 27, 2022

Comments

  • Hew Masters
    Hew Masters almost 2 years

    I have two C# applications, one is reading a file (File A) line by line and writing its contents to a different file (File B).

    The second application is using FileSystemWatcher for File B to see when it is updated and reporting the difference is line numbers between when the program was started and when the file was changed.

    Thats all I am trying to do for now, ultimately I want to read the lines between when the file was last read and the current read but until I can get the line difference that is on hold.

    The code that I have for application 1 is;

            static void Main(string[] args)
        {
            String line;
    
            StreamReader sr = new StreamReader("f:\\watch\\input.txt");
    
            FileStream fs = new FileStream("f:\\watch\\Chat.log", FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(fs);
    
            while ((line = sr.ReadLine()) != null)
            {
                sw.WriteLine(line);
                Thread.Sleep(200);
                Console.WriteLine(line);
                sw.Flush();
    
            }
    
            sw.Close();
            sr.Close();
    
        }
    

    The code that I have for application 2 is;

            public static int lines = 0;
    
        public static void Main()
        {
            Run();
        }
    
        public static void Run()
        {
            string[] args = System.Environment.GetCommandLineArgs();
    
            if (args.Length != 2)
            {
                Console.WriteLine("Usage: Watcher.exe (directory)");
                return;
            }
    
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = args[1];
    
    watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite 
       | NotifyFilters.FileName | NotifyFilters.DirectoryName;
    
    watcher.Filter = "Chat.log";
    
    watcher.Changed += new FileSystemEventHandler(OnChanged);
    
    watcher.EnableRaisingEvents = true;
    
    lines = File.ReadAllLines(args[1] + "\\Chat.log").Length;
    
    Console.WriteLine("File lines: " + lines);
    
    while(Console.Read()!='q');
    }
    
    private static void OnChanged(object source, FileSystemEventArgs e)
    {
        Linework(e.FullPath);
        Console.WriteLine("File: " +  e.FullPath + " " + e.ChangeType);
    }
    
    public static string Linework(string path)
    
    {
    
    
    
       string newstring = " ";
    
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        int newlines = File.ReadAllLines(path).Length;
        Console.WriteLine("Lines now: " + newlines);
    }
    
    return newstring;
    
    }
    

    Now when I try and run these two applications together I get an exception saying "Unhandled Exception: System.IO.IOException: The process cannot access the file because it is in use by another process".

    I have both filestreams setup for ReadWrite access and I have one of the filestreams setup for FileAccess.Write and the other for FileAccess.Read.

    Any clues as to why I would be getting this exception?

    Thanks Hew.