1

I'm working on a Form application which is suppose to run on multiple machines in one network at once. There is a Form in my application (let's call it Form1) from which you can edit a XML file located on the network. In order to prevent this file from being overwritten I have to make sure that only one person at a time can access Form1.

My first try was to create an attribute inside the XML file to indicate when someone is already accessing Form1. The problem with that solution was that in the case of a sudden crash like a power outage the attribute would not be changed back to its normal value because Form1 was never properly exited. So you would have to manually change the value inside the XML file.

My current solution is running a thread inside Form1 which is constantly reading a file until Form1 is closed again. And checking if the file is already being read before allowing other people to access Form1. This solution works fine but it's not pretty since I have to have an additional file which sole purpose is to be read since I can't constantly read the XML file itself without causing other problems.

Edit: Since the first answers are the same as my current solution here is the code of my current solution.

//CODE INSIDE FORM1

//Create thread which is reading File2
Thread readerThread = new Thread(new ThreadStart(ReadFile2));
readerThread.Start();

private void ReadFile2()
{
    using (FileStream stream = File.Open(pathFile2, FileMode.Open, FileAccess.Read, FileShare.None))
    {
        //Wait until Form1 is being closed
        threadWait.WaitOne();

        stream.Close();
    }
}

//CODE BEFORE ACCESSING FORM1

private bool Form1OpenCheck()
{
    //Check if file2 is being read
    bool noAccess = false;

    try
    {
        using (FileStream stream = File.Open(pathFile2, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    catch (IOException)
    {
        noAccess = true;
    }

    return noAccess;
}

I would appreciate it if anyone has a better solution for this problem. Thanks.

Ottxr
  • 153
  • 3
  • 13
  • https://stackoverflow.com/questions/53611950/write-text-to-file-system-io-ioexception – IndieGameDev Oct 28 '20 at 08:24
  • You will have a fundamental race condition if you try to check if the file is locked as a separate step before trying to use it. The code could find that the file is available, but before the checking method has returned, another process could lock the file. The best solution is to just try and change the file and catch and handle appropriate exceptions. – Matthew Watson Oct 28 '20 at 10:30
  • Does this answer your question? [Is there a way to check if a file is in use?](https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use) – Karl Knechtel Apr 07 '23 at 11:09

3 Answers3

0

I'm using this implementation (it's not mine, props to the dude who wrote this)

    /// <summary>
    /// Checks if a file is ready
    /// </summary>
    /// <param name="sFilename"></param>
    /// <returns></returns>
    public static bool IsFileReady(string sFilename)
    {
        // If the file can be opened for exclusive access it means that the file
        // is no longer locked by another process.
        try
        {
            using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                return inputStream.Length > 0;
            }
        }
        catch (Exception)
        {
            return false;
        }
    }
Skelvir
  • 41
  • 5
  • This is pretty much what I'm already doing in my current solution to check if the file is being read. But like I said I can't constantly read the XML file itself without causing other problems. So I was looking for another solution. – Ottxr Oct 28 '20 at 08:26
  • You could make Form1 open the XML file with FileShare.None, then if another user attempts to open the file, it will cause an Access Violation Exception. IMO that's prettier and more efficient than using a whole thread that is constantly doing nothing with another file. – Skelvir Oct 28 '20 at 08:39
  • The reason I can't do that is because I have to access the data inside the XML file for other parts of my program. If I would constantly have the XML file open inside Form1 those other parts wouldn't work anymore. That's why I created the second file in the first place. – Ottxr Oct 28 '20 at 08:50
  • Then maybe the FileStream.Lock method could be an alternative, where you only lock parts of a FileStream. Besides that, I guess your current solution may be decent enough. As far as I know, using Lock-files like you do isn't that uncommon. – Skelvir Oct 28 '20 at 09:19
0

You don't have to change the file to open it exclusively for you, just mention in your File.Open that no one may open it, not even for read-only:

string fileName = ...
using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
    // while here, no one else can access the file, not even delete it.
    ...
}
// now others can open the file again.

While you have the file open with FileShare.None, neither your process, nor another process can open the file, or access it any other way. They get an exception if they try to open or delete it. See FileShare Enum.

You or others can only open it after you Closed the Stream. Hence it is always good practice to enclose the opening in a using statement. Any exception will Close and Dispose the stream.

By the way, the overload of File.Open without a fileShare parameter also opens with FileShare.None.

So, it might be a good idea if in other use cases, you only want to read the file once, while others may still start changing it, maybe it is a good practice to open the file with FileShare.Read

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • The reason I constantly have the second file open inside Form1 is so other people can check if someone else is already in Form1 (_by trying to open the second file_) before they access Form1 them self. Once someone is inside Form1 they can edit the XML file (_first file_). – Ottxr Oct 28 '20 at 09:32
  • class System.Windows.Form is IDisposable, so you can be certain that it will be disposed, even if you crash. In Form.Dispose(bool) you can close the file. – Harald Coppoolse Oct 28 '20 at 10:05
0

Ok after talking to a colleague and a bit of brainstorming I came up with a better solution.

Inside Form1 I create a TCP Socket which is listening for connections to an IPEndPoint inside the network. Now in order to check if Form1 is already running on another machine I create another TCP Socket which is trying to connect to the same IPEndPoint. If the connection fails this means Form1 is running nowhere else and I can safely open it. If the connection works Form1 is already open somewhere and I can inform the user about it.

Ottxr
  • 153
  • 3
  • 13