Open file ReadOnly - c#

Currently, this is how I'm opening a file to read it:
using (TextReader reader = new StreamReader(Path.Combine(client._WorkLogFileLoc, "dump.txt")))
{
//do stuff
}
How can I open the file in ReadOnly mode, so that if another process has the file open at the same time, my program can still read it.

The typical problem is that the other process has the file open for writing. All of the standard File methods and StreamReader constructors open the file with FileShare.Read. That cannot work, that denies write sharing. You cannot deny writing, the other process was first and got write access. So you'll be denied access instead.
You have to use FileShare.ReadWrite, like this:
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (var sr = new StreamReader(fs))
{
// etc...
}
Beware that you'll still have a tricky problem, you are reading a half-written file. The other process flushes data to the file at random points in time, you may well read only half a line of text. YMMV.

If you want to open the file read-only, try this:
using (TextReader reader
= new StreamReader(File.OpenRead(Path.Combine(client._WorkLogFileLoc, "dump.txt"))))
{
//do stuff
}
Notice the call to File.OpenRead().

You can set the file attribute by calling File.SetAttributes
string path = Path.Combine(client._WorkLogFileLoc, "dump.txt");
FileAttributes curAttributes = File.GetAttributes(path);
File.SetAttributes(path, curAttributes | FileAttributes.ReadOnly);

Per https://learn.microsoft.com/en-us/dotnet/api/system.io.file.openread?redirectedfrom=MSDN&view=netcore-3.1#System_IO_File_OpenRead_System_String_
File.OpenRead enables read shared access not read/write. This prevents the "other process" from being able to close/reopen/write more data as xbonez wants to permit. hans-passant addresses what was requested.
Per the referenced documentation: This method is equivalent to the FileStream(String, FileMode, FileAccess, FileShare) constructor overload with a FileMode value of Open, a FileAccess value of Read and a FileShare value of Read.

Related

OK to have multiple processes reading from the same text file?

I've inherited development responsibilities for a set of .NET projects which include Windows services, an ASP.NET website and standard executables which are launched via task manager.
All of these components connect to a MySQL database. Currently, the encrypted connection string is located in the individual app.config/web.config files. I think it's kind of a pain to have to update the connection string in multiple places, so I had the idea to put the connection string in a text file which would be accessed by all the individual components.
Before I move full-steam ahead on that, I wanted to ask if I might be introducing a bug since there could potentially be multiple processes trying to access this text file at the same time. I've written a universal method for reading the text file, and I was careful to ensure that the file is being opened in "Read" mode:
public string AESDecryptFromFile(string path, string password, string IV)
{
if (!File.Exists(path))
throw new Exception("File not found: " + path);
string cryptogram;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
cryptogram = sr.ReadToEnd().Trim(new char[] { '\r', '\n', ' ' });
}
}
return AESDecrypt(cryptogram, password, IV);
}
I don't have any reason to believe that the processes would lock the file if I implement it this way, but I thought I'd ask.
Thanks in advance!
By default with the (string path, FileMode mode, FileAccess access) constructor of FileStream will get the following configuration as well:
FileShare = FileShare.Read
bufferSize = 4096
useAsync = false
Because you don't want the process to lock the file, this will be perfect for you as you'll require Read only (See https://msdn.microsoft.com/en-us/library/system.io.fileshare(v=vs.110).aspx)
However if you want to be explicit you can use the following constructor:
FileStream s = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);
Until you do not change the file it's OK. It makes problems when you want to write or write and read at multiple threads, because you don't know which one thread will write/read end/start first so you can't predict any behaviour
I think you might need to tell your FileStream constructor how you want to share the file? "fileshare"

File.Open for read access denied on executing file in Windows

I have a problem with file permissions on executing files in Windows that seems to be solved after following a forum tip[1], but I cannot understand why. Maybe you guys can help.
I'm checking the banner of a file by executing it (reading the console output) and then opening the same file for reading afterwards using FileStream:
public void fileMD5(string filename) {
if (!File.Exists(filename)) return NT.Fail("File does not exist: " + filename);
BinaryReader stream = new BinaryReader(File.Open(filename,
FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int readBytes;
while ((readBytes = stream.Read(buffer, 0, bufferSize)) > 0) {
md5Hasher.TransformBlock(buffer, 0, readBytes, buffer, 0);
}
stream.Close();
}
fileMD5('sample.exe');
and every once in a while I would get "file is being used by another process". From Wikipedia I know that Windows will set a lock on executing files denying write access[2], but I'm only reading. Also the process should have stopped already when I try to open it.
From the forum post it would seem that adding a FileShare.ReadWrite would help and it seems that It does:
FileStream stream = File.Open('sample.exe',
FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
But I don't understand why. Is there a race condition here that I don't see?
Also the File.Open call seems to be much quicker with FileShare.ReadWrite instead of the default (which I guess is FileShare.Read).
[1] http://www.xtremevbtalk.com/archive/index.php/t-192118.html
[2] http://en.wikipedia.org/wiki/File_locking#In_Microsoft_Windows
When you do not specify a FileShare parameter the default for this option is FileShare.None, in fact the code within the File class simply executes this:
public static FileStream Open(string path, FileMode mode, FileAccess access)
{
return File.Open(path, mode, access, FileShare.None);
}
With regards to the performance I can only imagine that specifying FileShare.ReadWrite means that Windows does not need to aquire a lock on the file.
As far as the "file is being used by another process" error you are getting does this issue go away if you wrap the usage of the stream variable within a using block so that the Stream gets disposed of as soon as you are done?
using (var stream = File.Open('sample.exe', FileMode.Open, FileAccess.Read))
{
//do something with the stream here
}
You should close your FileStream and then afterwards open a new FileStream.
FileShare is needed when applications want to share a file not only one application or an application has multiple readers or writers at the same time.
Why? It's get messy when everyone can read and write at the same time. In this case you should better explicitly set it so its clear that its get messy. :)
It has to do with the underlying windows API CreateFile flags.
See http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx
for an overview,
http://blogs.msdn.com/b/larryosterman/archive/2004/05/13/131263.aspx
for en explanation on how NT (and following) load the execs with FILE_SHARE_DELETE
And especially this
http://blogs.msdn.com/b/oldnewthing/archive/2004/05/11/129759.aspx
For a great explanation on how the share permission and desired access compose together!
It seems like specifying the wrong FileShare can prohibit you from accessing a file. If you specify FileShare.Read but some other application has currently write access to that file, you cannot access the file, since your FileShare.Read is currently not satisfiable. FileShare.ReadWrite is less restrictive, because its easier satisfiable. Source: http://blogs.msdn.com/b/oldnewthing/archive/2004/05/11/129759.aspx

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

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?
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
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.
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);
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!

Easiest way to read text file which is locked by another application

I've been using File.ReadAllText() to open a CSV file, but every time I forget to close the file in Excel, the application throws an exception because it can't get access to the file.
(Seems crazy to me, I mean the READ in ReadAllText seems pretty clear)
I know that there is File.Open with all the bells and whistles, but is there an 'intermediate' method which doesn't involve messing around with buffers and char arrays?
I think you just want the following:
using (var fileStream = new FileStream("foo.bar", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var textReader = new StreamReader(fileStream))
{
var content = textReader.ReadToEnd();
}
The FileAccess.Read parameter is what is important, to indicate that you only want to read the file. Of course, even to do this, the file must have been opened by Excel in read-share mode (see the FileShare enum in .NET). I haven't tested, so I can't guarantee that Excel does this, though I would expect it does.
[edit]
Here's a method version:
static string ReadAllText(string file)
{
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var textReader = new StreamReader(fileStream))
return textReader.ReadToEnd();
}
If you want to specify file sharing flags in order to open a file that's in use, you're stuck with File.Open().

In C#, if 2 processes are reading and writing to the same file, what is the best way to avoid process locking exceptions?

With the following file reading code:
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
using (TextReader tr = new StreamReader(fileStream))
{
string fileContents = tr.ReadToEnd();
}
}
And the following file write code:
using (TextWriter tw = new StreamWriter(fileName))
{
tw.Write(fileContents);
tw.Close();
}
The following exception details are seen:
The process cannot access the file
'c:\temp\myfile.txt' because it is
being used by another process.
What is the best way of avoiding this? Does the reader need to retry upon receipt of the exception or is there some better way?
Note that the reader process is using a FileSystemWatcher to know when the file has changed.
Also note that, in this instance, I'm not looking for alternatives ways of sharing strings between the 2 processes.
You can open a file for writing and only lock write access, thereby allowing others to still read the file.
For example,
using (FileStream stream = new FileStream(#"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
// Do your writing here.
}
Other file access just opens the file for reading and not writing, and allows readwrite sharing.
using (FileStream stream = new FileStream(#"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Does reading here.
}
If you want to ensure that readers will always read an up-to-date file, you will either need to use a locking file that indicates someone is writing to the file (though you may get a race condition if not carefully implemented) or make sure you block write-sharing when opening to read and handle the exception so you can try again until you get exclusive access.
If you create a named Mutex you can define the mutex in the writing application, and have the reading application wait until the mutex is released.
So in the notification process that is currently working with the FileSystemWatcher, simply check to see if you need to wait for the mutex, if you do, it will wait, then process.
Here is a VB example of a Mutex like this that I found, it should be easy enough to convert to C#.
Get your process to check the status of the file if it is being written to. You can do this by the presence of a lock file (i.e. the presence of this other file, which can be empty, prevents writing to the main file).
Even this is not failsafe however, as the two processes may create the lock file at the same time - but you can check for this before you commit the write.
If your process encounters a lock file then get it to simply sleep/wait and try again at a predefined interval in the future.
Is there any particular reason for opening the file with FileShare.None? That'll prevent the file from being opened by any other process.
FileShare.Write or FileShare.ReadWrite should allow the other process (subject to permissions) to open and write to the file while you are reading it, however you'll have to watch for the file changing underneath you while you read it - simply buffering the contents upon opening may help here.
All of these answers, however, are equally valid - the best solution depends on exactly what you're trying to do with the file: if it's important to read it while guaranteeing it doesn't change, then lock it and handle the subsequent exception in your writing code; if it's important to read and write to it at the same time, then change the FileShare constant.
You can use a Mutex object for this.
The reader and writer both need retry mechanisms. Also FileShare should be set to FileShare.read for the readers and FileShare.none for the writer. This should ensure that the readers don't read the file while writing is in progress.
The reader (excluding retry) becomes
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (TextReader tr = new StreamReader(fileStream))
{
string fileContents = tr.ReadToEnd();
}
}
The writer (excluding retry) becomes:
FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
using (TextWriter tw = new StreamWriter(fileStream))
{
tw.Write(fileContents);
tw.Close();
}
Write to a temp file, when finished writing rename/move the file to the location and/or name that the reader is looking for.
The best thing to do, is to put an application protocol on top of a file transfer/ownership transfer mechanism. The "lock-file" mechanism is an old UNIX hack that has been around for ages. The best thing to do, is to just "hand" the file over to the reader. There are lots of ways to do this. You can create the file with a random file name, and then "give" that name to the reader. That would allow the writer to asynchronously write another file. Think of how the "web page" works. A web page has a "link" to more information in it, for images, scripts, external content etc. The server hands you that page, because it's a coherent view of the "resource" you want. Your browser then goes and gets the appropriate content, based on what the page description (the HTML file or other returned content), and then transfers what it needs.
This is the most resilient type of "sharing" mechanism to use. Write the file, share the name, move to the next file. The "sharing the name" part, is the atomic hand off that makes sure that both parties (the reader and the writer) agree that the content is "complete."

Categories