111

I have a windows service writes its log in a text file in a simple format.

Now, I'm going to create a small application to read the service's log and shows both the existing log and the added one as live view.

The problem is that the service locks the text file for adding the new lines and at the same time the viewer application locks the file for reading.

The Service Code:

void WriteInLog(string logFilePath, data)
{
    File.AppendAllText(logFilePath, 
                       string.Format("{0} : {1}\r\n", DateTime.Now, data));
}

The viewer Code:

int index = 0;
private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                using (StreamReader sr = new StreamReader(logFilePath))
                {
                    while (sr.Peek() >= 0)  // reading the old data
                    {
                        AddLineToGrid(sr.ReadLine());
                        index++;
                    }
                    sr.Close();
                }

                timer1.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }


private void timer1_Tick(object sender, EventArgs e)
        {
            using (StreamReader sr = new StreamReader(logFilePath))
            {
                // skipping the old data, it has read in the Form1_Load event handler
                for (int i = 0; i < index ; i++) 
                    sr.ReadLine();

                while (sr.Peek() >= 0) // reading the live data if exists
                {
                    string str = sr.ReadLine();
                    if (str != null)
                    {
                        AddLineToGrid(str);
                        index++;
                    }
                }
                sr.Close();
            }
        }

Is there any problem in my code in reading and writing way?

How to solve the problem?

Homam
  • 23,263
  • 32
  • 111
  • 187
  • Possible duplicate of [Can I prevent a StreamReader from locking a text file whilst it is in use?](https://stackoverflow.com/questions/1606349/can-i-prevent-a-streamreader-from-locking-a-text-file-whilst-it-is-in-use) – Gerardo Lima Sep 15 '17 at 09:49

7 Answers7

141

You need to make sure that both the service and the reader open the log file non-exclusively. Try this:

For the service - the writer in your example - use a FileStream instance created as follows:

var outStream = new FileStream(logfileName, FileMode.Open, 
                               FileAccess.Write, FileShare.ReadWrite);

For the reader use the same but change the file access:

var inStream = new FileStream(logfileName, FileMode.Open, 
                              FileAccess.Read, FileShare.ReadWrite);

Also, since FileStream implements IDisposable make sure that in both cases you consider using a using statement, for example for the writer:

using(var outStream = ...)
{
   // using outStream here
   ...
}

Good luck!

Manfred
  • 5,320
  • 3
  • 35
  • 29
33

Explicit set up the sharing mode while reading the text file.

using (FileStream fs = new FileStream(logFilePath, 
                                      FileMode.Open, 
                                      FileAccess.Read,    
                                      FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (sr.Peek() >= 0) // reading the old data
        {
           AddLineToGrid(sr.ReadLine());
           index++;
        }
    }
}
Indy9000
  • 8,651
  • 2
  • 32
  • 37
heads5150
  • 7,263
  • 3
  • 26
  • 34
  • 2
    it is unnecessary to explicitly close the stream, as StreamReader.Dispose closes it automatically. – nothrow Aug 10 '10 at 11:16
  • 2
    I believe the file share should be set on BOTH read and write streams, and not only read. – L.E.O Aug 10 '10 at 11:19
  • StreamReader.Dispose _also_ disposes of the FileStream if I'm not mistaken. Here it's disposed twice. – Eregrith May 03 '19 at 07:52
15
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

-> this doesn't lock the file.

Indy9000
  • 8,651
  • 2
  • 32
  • 37
nothrow
  • 15,882
  • 9
  • 57
  • 104
  • 1
    Seems to work when reading data. When writing the locked file error comes up. – webzy Feb 08 '16 at 09:00
  • @webzy of course. You cannot write to a file without locking it – Fandango68 Dec 09 '21 at 01:36
  • @Fandango68 The accepted answer elaborates on the solution better than mine, but your statement is completely not true. It is possible to write to files without holding any lock. – nothrow Dec 09 '21 at 16:34
  • !! surprised. I should try it. So how does Windows update a log file in real time, whilst running the above? – Fandango68 Dec 09 '21 at 23:51
10

The problem is when you are writing to the log you are exclusively locking the file down so your StreamReader won't be allowed to open it at all.

You need to try open the file in readonly mode.

using (FileStream fs = new FileStream("myLogFile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (!fs.EndOfStream)
        {
            string line = fs.ReadLine();
            // Your code here
        }
    }
}
James
  • 80,725
  • 18
  • 167
  • 237
  • As now I know, `File.ReadAllText()` fails with read-only mode. – bohdan_trotsenko Oct 17 '13 at 14:27
  • @modosansreves yeah I removed that portion of the answer as the [docs](http://msdn.microsoft.com/en-us/library/ms143368(v=vs.100).aspx) state that it will throw an `UnauthorizedAccessException` if the file is read only - must have missed that at the time of answering! – James Oct 17 '13 at 14:33
5

I remember doing the same thing a couple of years ago. After some google queries i found this:

    FileStream fs = new FileStream(@”c:\test.txt”, 
                                   FileMode.Open, 
                                   FileAccess.Read,        
                                   FileShare.ReadWrite);

i.e. use the FileShare.ReadWrite attribute on FileStream().

(found on Balaji Ramesh's blog)

dotmartin
  • 531
  • 1
  • 4
  • 25
1

Have you tried copying the file, then reading it?

Just update the copy whenever big changes are made.

Tom Gullen
  • 61,249
  • 84
  • 283
  • 456
  • 2
    I've tried this and it's not the best solution. Copying the file takes too long, for a 4mb file takes like 20 seconds. – Seichi May 25 '15 at 14:48
-4

This method will help you to fastest read a text file and without locking it.

private string ReadFileAndFetchStringInSingleLine(string file)
    {
        StringBuilder sb;
        try
        {
            sb = new StringBuilder();
            using (FileStream fs = File.Open(file, FileMode.Open))
            {
                using (BufferedStream bs = new BufferedStream(fs))
                {
                    using (StreamReader sr = new StreamReader(bs))
                    {
                        string str;
                        while ((str = sr.ReadLine()) != null)
                        {
                            sb.Append(str);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        catch (Exception ex)
        {
            return "";
        }
    }

Hope this method will help you.

Amit Kumawat
  • 574
  • 5
  • 11
  • 5
    As far as I understand, this method reads a whole file into a string and swallows exceptions along the way. I can't see how is that supposed to help with file locking. – default locale Jan 25 '19 at 12:32