Prevent write access to XML file from EXE duplicate - c#

So I have this WinForms EXE (VS2010) which reads and writes from / to a single XML file like this:
// read
FileStream dbXmlFileStream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
myDataSet.ReadXml(dbXmlFileStream);
dbXmlFileStream.Close();
// write
myDataSet.AcceptChanges();
StreamWriter xmlSW = new StreamWriter(path);
myDataSet.WriteXml(xmlSW, XmlWriteMode.IgnoreSchema);
xmlSW.Close();
The XML file lives on a networked drive and is shared by multiple instances of the same EXE (also on the same networked path). So each user has a shortcut on their machine pointing to the same EXE.
Whoever accesses the EXE first ("first" as in the 1st EXE instance running) should get exclusive write access to the XML. Subsequent EXE instances should be able to read (i.e. throw no errors when opening the EXE) but get a locking error when trying to save into the same XML via their EXE instance.
With my code, there are no exceptions thrown while multiple users read or write into the file. However, it's misleading because in the case where all of them attempted to save something at the same time, only the most recent save persists on the disk.
How can I remove ambiguity and actually capture write lock exceptions for any subsequent users?
I tried different variations of FileAccess and FileShare enums in File.Open with no luck.

Related

Binary Reader with Filemode.Open Blocks file for other programs

I am trying to make a tagging solution for .wav audiofiles.
For that reason I need to open the file to read out it's Tags.
The code for that starts out like this:
this.Reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read));
this.RiffId = Encoding.ASCII.GetString(Reader.ReadBytes(4)); // excluding 8 bit header
this.FileSize = Reader.ReadInt32(); // size of entire file (4 bytes)
this.FileType = Encoding.ASCII.GetString(Reader.ReadBytes(4));
From my understanding, this will open the File readonly normally? However, when I open the audio file while it is open in my application, media players spit out an error stating that the file is beeing blocked by my program:
I whish to have the file readonly, writing will always be a full Copy of the file.
Having the possibility to read the file in two programs at a time is necessary when listening to the audio in order to tag the file.
Closing the file while listening to it is not so easily possible as I want to keep my application responsive. Additional data from the riff chunks is loaded and stored in buffer when nessesary only.
this.Reader = new BinaryReader(File.Open(path,FileMode.Open,FileAccess.Read,FileShare.Read));
FileMode.Open specifies, how to proceed with the file. More on that on https://learn.microsoft.com/en-us/dotnet/api/system.io.filemode?view=net-5.0
some examples:
Open 3
Specifies that the operating system should open an existing file. The ability to open the file is dependent on the value specified by the FileAccess enumeration. A FileNotFoundException exception is thrown if the file does not exist.
Append 6
Opens the file if it exists and seeks to the end of the file, or creates a new file. This requires Append permission. FileMode.Append can be used only in conjunction with FileAccess.Write. Trying to seek to a position before the end of the file throws an IOException exception, and any attempt to read fails and throws a NotSupportedException exception.
Truncate 5
Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes. This requires Write permission. Attempts to read from a file opened with FileMode.Truncate cause an ArgumentException exception.
FileAccess specifies your own file access
https://learn.microsoft.com/en-us/dotnet/api/system.io.fileaccess?view=net-5.0
FileShare specifies the access other threads have to the file
https://learn.microsoft.com/en-us/dotnet/api/system.io.fileshare?view=net-5.0

Reading rotation log files and file locking

I have a Python service spitting out logs to text files. It rotates them every ~400KB. So the Python service opens up a handle on the file, let's call it app.log. It then writes content to the file every now and again flushes it to the disk. When it reaches a certain size, it closes it's handle, and move it to app.log.1 and starts a new handle on app.log.
So I can't change this service, but I have a C# application that will read those logs. I ran into 3 scenarios:
If I just try to read those those logs using new FileStream(path, FileMode.Open);, it won't allow me as the Python service has an handle on it.
If I try to open it using new FileStream(path, FileMode.Open, FileAccess.Read);, this allows me to read it, but if the service tries to rotate the log, it won't be able to as my C# application now has a handle on the file.
And if I try to open the file using new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Delete);, my Python service won't fail on deleting the file, but it will fail creating a new handle on app.log as the C# application would still have a handle on it.
The only solution which I'm aware of would be using Windows Shadow Copy (VSS) to create a snapshot of the logs and then read that snapshot but this would be quite expensive as we need to query the logs at every 5 minutes.
Also, I'm not interested in reading the rotated logs, app.log.1, app.log.2 etc.
Logging to text files under Windows seems to be a pain what with all the locking/handles. Does anyone have any suggestion?
You should be able to open your file as Dmitry Popov suggested in his answer as below and not affect Python writing to it, however it depends upon what locks the Python application holds on the file, it can lock you out completely and there is nothing you to do to prevent that without hacking Windows.
FileSream fs = File.Open(#"c:\Test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)
The FileStream object created in this way will still be connected to the same file after an operating system file move operation has been performed on it to rename it.
So lets assume your python app opens a file called Test.log and starts writing to it. You can read any data written to it (after python flushes its buffer) using the file stream returned from the line above. The python application can close and reopen the file as often as it wants writing each time and the reading application will remain connected to it. When the python application issues a File Move operation to rename the file to Test1.log, the file stream returned above will still be connected to the file which is now called Test1.log so you can continue reading to the end of the file before starting the new log file if that is what you want. There is one caveat to this. The Python application needs to use a Move/Rename operation rather than copying the file to a new one and deleting the old one, I'd be surprised if that is what it does though.
There is a possibility that your reading application will reach the end of the file before your writing application has finished reading from it. In this case fs.Read will keep returning 0 after a timeout until the writing application opens the file and writes some more. You can make the time out very long / infinite if you want.
As you don't want to read to the end of one file before starting the new one you could just close and reopen the file at regular intervals. The log file without the numeric suffix should always be the most recent.
If however you want your reading application to read to the end of one log file before starting at the beginning of the next one you will need to work out when the writing application has finished writing to the log file. Also it needs to find out what the file is now called so it can read n-1 next. Is there some marker written by the python application that you could look for to denote the end of a file? Does it write 'End Of Log' or something similar?
Be warned also that there are going to be short periods of time when LogFile n-1 does not exist. This is because if you have log files 0, 1, 2 and 3 it needs to make log file 3 into log file 4 before it can make log file 2 into log file 3. While it is doing this there will be a short period of time when you have log files 0, 1, 2, 4 and no 3.
Personally I would find the developer that wrote the logging for your Python application give him/her the evil eye for causing this headache in the first place. What is wrong with having the most recent log file have the largest number?
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
//Do works
}
C# thread don't lock the file in this case, your Python script can write and close the file to create another one without deadlock.
You can combine FileShare flags:
FileShare.Write | FileShare.Delete
Here's a demo:
using (var cSharp = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Write | FileShare.Delete))
{
// The Python service will be able to change and to rename the file:
using (var python = new FileStream(filename, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}
File.Move(filename, newFilename);
}
You will have to deal with concurrency. You can use FileSystemWatcher to monitor file changes.

C# Copy a File That is Being Written To

I have read many other posts about this topic, but none appear to solve my problem directly (which surprises me).
Regardless...I wrote a log parser and very simply I am looking to copy a file from a remote machine locally, prior to parsing it. The file I am trying to copy is being written to constantly and I have ‘random’ success in copying it. Sometimes it will work and other times I will get an ‘access is denied’ or FileAccess error. A few other points:
Whenever I use windows explorer to copy the file locally, I never
have a problem copying it (which leads me to believe it’s perfectly
possible to copy the file 100% of the time).
I can always open the file using a text editor in its remove location.
I do not own the file being written to and do not wish to ‘lock’ it in anyway such that the application that is actually writing to this file fails.
Does anyone have any suggestions for how to copy this file?
The current command I am using is:
File.Copy(this.txt_log_file_to_analyze.Text, sLogFileToAnalyze,true);
I guess you'll have to open the file using:
File.Open(this.txt_log_file_to_analyze.Text,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)
and then copy the contents of the file 'manually' i.e.
using (var from = File.Open("path", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var to = File.OpenWrite("to_path"))
{
from.CopyTo(to);
}
or if .NET 4.5 see How do I copy the contents of one stream to another?
Using the above api, you can specify that you do not want exclusive access to the file.

How do you read a file which is in use? [duplicate]

This question already has answers here:
How can I read a file even when getting an "in use by another process" exception?
(4 answers)
Closed 6 years ago.
I have a small problem. I have a tool which should parse a log file daily, unfortunately this log file is in use by the process which writes to the log and I cannot stop it.
First try was to create a copy of the file, which is not working either.
Is there any way for me to read the current text of the log file, even if it is already in use?
using (FileStream stream = File.Open("path to file", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
}
}
}
The FileAccess specifies what YOU want to do with the file.
The FileShare specifies what OTHERS can do with the file while you have it in use.
In the example above, you can open a file for reading while other processes can have the file open for read/write access. In most cases, this will work for opening logfiles that are in use.
You are at the mercy of the program that is writing the file. In Windows, a process can open a file for reading, writing or both, but it can also control whether other processes can open the file for reading, writing or both. If the other process has denied you the right to read the contents of the file, then there is nothing you can do about it.
If you control the source code of the program that is writing the log file, then change it to allow read access by other processes.
Use File.OpenRead(path), this allows you to access a readonly stream to the file; that way you won't be bothered if another application has a write lock on the file.
it depends, have you tried reading the file in read only? using one of the static methods
System.IO.File.ReadAllText(path) or System.IO.File.ReadAllLines(path)
they may work if there file isn't locked exclusively
I highly recommend BareTail, which we use to look at all of our logs in real time. Also supports highlighting, which is very useful.

How can I lock a file while writing to it via a FileStream?

I am trying to figure out how to write a binary file with a FileStream and BinaryWriter, and keep the file locked for read while I am writing. I specifically don't want other applications/processes to be able to read from the while while its being written to.
//code to declare ba as a byte array
//dpath is the path to the file
FileStream BinaryFile = new FileStream(dpath, FileMode.Create, FileAccess.Write);
BinaryWriter Writer = new BinaryWriter(BinaryFile);
Writer.Write(ba);
Writer.Close();
BinaryFile.Dispose();
Now the problem is the file can be opened by other applications during the write, which is undesirable in my current application. The FileStream has a Lock Method, but that locks for writing and not for reading, so that doesn't help me.
You're looking for the fourth parameter of the FileStream Constructor.
public FileStream(
string path,
FileMode mode,
FileAccess access,
FileShare share
)
So in your case:
FileStream BinaryFile = new FileStream(dpath, FileMode.Create,
FileAccess.Write, FileShare.None);
FileShare-Enum:
Contains constants for controlling the kind of access other FileStream
objects can have to the same file.
Members:
None, Declines sharing of the current file. Any request to open the file (by this process or another process) will fail until the file is closed.
Read, Allows subsequent opening of the file for reading. If this flag is not specified, any request to open the file for reading (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
Write, Allows subsequent opening of the file for writing. If this flag is not specified, any request to open the file for writing (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
ReadWrite, Allows subsequent opening of the file for reading or writing. If this flag is not specified, any request to open the file for reading or writing (by this process or another process) will fail until the file is closed. However, even if this flag is specified, additional permissions might still be needed to access the file.
Delete, Allows subsequent deleting of a file.
Inheritable, Makes the file handle inheritable by child processes. This is not directly supported by Win32.
I don't know if it is possible.
For instance Firefox stores files while downloading using another name until they are finished. When everything is there, it renames it to the original name. A different file suffix will avoid that users try to open it with a double click.

Categories