113

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?

1

7 Answers 7

145

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!

Sign up to request clarification or add additional context in comments.

4 Comments

There's also the ReadLines() version at stackoverflow.com/questions/5338450/…
Perfect - I had thought File.Open() with FileAccess.Read was enough, but it is not. This is, though. :)
On the writer, wouldn't FileShare.Read be enough since there is only the one writer?
Also, worth noting that FileStream implements IDisposable
34

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

3 Comments

it is unnecessary to explicitly close the stream, as StreamReader.Dispose closes it automatically.
I believe the file share should be set on BOTH read and write streams, and not only read.
StreamReader.Dispose also disposes of the FileStream if I'm not mistaken. Here it's disposed twice.
16
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

-> this doesn't lock the file.

4 Comments

Seems to work when reading data. When writing the locked file error comes up.
@webzy of course. You cannot write to a file without locking it
@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.
!! surprised. I should try it. So how does Windows update a log file in real time, whilst running the above?
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 (!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            // Your code here
        }
    }
}

2 Comments

As now I know, File.ReadAllText() fails with read-only mode.
@modosansreves yeah I removed that portion of the answer as the docs state that it will throw an UnauthorizedAccessException if the file is read only - must have missed that at the time of answering!
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)

Comments

1

Have you tried copying the file, then reading it?

Just update the copy whenever big changes are made.

1 Comment

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.
-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.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.