How can I read a file even when getting an "in use by another process" exception?
Asked Answered
S

5

61

In VB.NET or C#, I'm trying to read the contents of a text file that is in use by another program (that's the point, actually, I can't stop the program or it stops writing to the text file, and I want to periodically read out what is currently in the text file in another program).

This is the code I'm using (VB.NET)

Dim strContents As String
Dim objReader As StreamReader
objReader = New StreamReader(FullPath)
strContents = objReader.ReadToEnd()
objReader.Close()

Or in C#:

var objReader = new StreamReader(FullPath);
var strContents = objReader.ReadToEnd();
objReader.Close();

The above, however, throws the IO exception "The process cannot access the file 'file.txt' because it is being used by another process." Are there any workarounds in this scenario?

Spoken answered 9/12, 2010 at 16:22 Comment(0)
S
93
FileStream logFileStream = new FileStream("c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader logFileReader = new StreamReader(logFileStream);

while (!logFileReader.EndOfStream)
{
    string line = logFileReader.ReadLine();
    // Your code here
}

// Clean up
logFileReader.Close();
logFileStream.Close();

Original source for code

Spokeswoman answered 9/12, 2010 at 16:25 Comment(5)
Hi, not sure if someone will read this, perhaps i'd be better creating my own post, but... I've tried this solution and it still throws the error that it is in use by another process. Any ideas why this would happen? Cheers, Dave.Chantellechanter
Make sure you have FileShare.ReadWrite, even if you are only reading. That was the magic I was missing until I came here.Frere
As per yoyo's comment, FileShare.ReadWrite (the fourth argument) is critical. Be sure to not confuse it with FileAccess.ReadWrite.Holifield
Worked for me including the last parameter FileShare.ReadWriteCarol
Note that some processes will want the ability to delete the file as well. If you want completely wide open file sharing then you need to use FileShare.ReadWrite Or FileShare.Delete. This will allow you to read from the file until it's deleted by the other process.Leucomaine
S
10

I'll do the fish. The FileShare mode is critical, you must allow for write sharing. That cannot be denied since the process that is writing the file already obtained write access. The StreamReader() constructor uses FileShare.Read and doesn't have an option to use a different value. Using the StreamReader(Stream) constructor is instead is indeed the workaround.

Beware however that this sharing mode also has implications for your code. You cannot predict when the other process flushes the file. The last line you read may contain only part of a line of text. When it flushes is file buffer again, later, you'll get the rest of the line. Clearly this can mess up your logic.

Suspiration answered 9/12, 2010 at 16:44 Comment(0)
C
5

It depends on the FileShare mode with which the file was opened by the other application that is appending to the file. When the other application was opening the file, it specified a FileShare mode for other applications to access the file. This FileShare mode could have been read, write, both, delete, all of these, or none.

You have to specify the very same FileShare mode that the other application specified. If the other application allowed only reading, use FileShare.Read; if it allowed both reading and writing, use FileShare.ReadWrite.

StreamReader uses only FileShare.Read mode, so you can already assume that that's not the right one. So, try ReadWrite, like so:

FileStream fs = new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader reader = new StreamReader(fs);
Corinthian answered 26/8, 2012 at 4:18 Comment(1)
I think this is the crux of the problem. If someone else has chosen to exclusively open a file, regardless of your parameters, you won't be able to open it. It seems the best solution is to attempt to open with FileShare.ReadWrite so you you don't prevent other processes, but still use a try/catch in case someone else already has it locked.Thermoluminescent
Q
2

Not sure how this will behave with an already open file, but this will prevent your application from locking it:

FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader sr = new StreamReader(fs);

Hope it helps!

Quinlan answered 9/12, 2010 at 16:28 Comment(0)
T
0

I pried this code out of ChatGPT, and it works! First I asked for C# solution, no luck, then asked for C++, good luck, then asked it to do the same in C#, here is the untouched code. Edit: the accepted answer also works, weird thought i tried that.

using System;
using System.IO;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CreateFile(
        string lpFileName,
        FileAccess dwDesiredAccess,
        FileShare dwShareMode,
        IntPtr lpSecurityAttributes,
        FileMode dwCreationDisposition,
        FileAttributes dwFlagsAndAttributes,
        IntPtr hTemplateFile
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadFile(
        IntPtr hFile,
        byte[] lpBuffer,
        uint nNumberOfBytesToRead,
        out uint lpNumberOfBytesRead,
        IntPtr lpOverlapped
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hObject);

    static void Main()
    {
        IntPtr fileHandle = CreateFile(
            "path/to/file.txt",
            FileAccess.Read,
            FileShare.ReadWrite,
            IntPtr.Zero,
            FileMode.Open,
            FileAttributes.Normal,
            IntPtr.Zero
        );

        if (fileHandle != IntPtr.Zero && fileHandle != new IntPtr(-1))
        {
            try
            {
                FileInfo fileInfo = new FileInfo("path/to/file.txt");
                byte[] buffer = new byte[fileInfo.Length];

                if (ReadFile(fileHandle, buffer, (uint)fileInfo.Length, out uint bytesRead, IntPtr.Zero))
                {
                    string content = System.Text.Encoding.Default.GetString(buffer, 0, (int)bytesRead);
                    Console.WriteLine(content);
                }
            }
            finally
            {
                CloseHandle(fileHandle);
            }
        }
        else
        {
            Console.WriteLine("Failed to open file");
        }
    }
}
Tungsten answered 25/6, 2023 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.