检查文件是否已被其他进程使用

Check if file is already in use by other process

我正在开发一个 Form 应用程序,它应该 运行 同时在一个网络中的多台机器上运行。我的应用程序中有一个表单(我们称它为 Form1),您可以从中编辑位于网络上的 XML 文件。为了防止这个文件被覆盖我必须确保一次只有一个人可以访问 Form1.

我的第一个尝试是在 XML 文件中创建一个属性来指示何时有人已经在访问 Form1。该解决方案的问题在于,在突然崩溃(如停电)的情况下,属性不会更改回其正常值,因为 Form1 从未正确退出。因此,您必须手动更改 XML 文件中的值。

我当前的解决方案 是运行在Form1 中设置一个线程,该线程不断读取文件直到Form1 再次关闭。并在允许其他人访问之前检查文件是否已被读取 Form1。这个解决方案工作正常,但它并不漂亮,因为我必须有一个额外的文件,它的唯一目的是读取,因为我不能不断地读取 XML 文件本身而不会引起其他问题。

编辑: 由于第一个答案与我当前的解决方案相同,这里是我当前解决方案的代码.

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

如果有人对此问题有更好的解决方案,我将不胜感激。谢谢

我正在使用这个实现(这不是我的,支持写这个的人)

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

您不必更改文件以专门为您打开它,只需在您的 File.Open 中提及没有人可以打开它,甚至 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.

当您使用 FileShare.None 打开文件时,您的进程或其他进程都无法打开该文件,或以任何其他方式访问它。如果他们试图打开或删除它,他们会得到一个例外。参见 FileShare Enum

您或其他人只有在您关闭流后才能打开它。因此,将开头包含在 using 语句中始终是一个好习惯。任何异常都将关闭并处置流。

顺便说一下,没有 fileShare 参数的 overload of File.Open 也可以用 FileShare.None 打开。

因此,如果在其他用例中,您只想读取文件一次,而其他人可能仍会开始更改它,这可能是个好主意,也许用 [=27 打开文件是个好习惯=]

好的,在与同事交谈并集思广益后,我想出了一个更好的解决方案

Form1 内部,我创建了一个 TCP Socket,它正在侦听与网络内部 IPEndPoint 的连接。现在为了检查 Form1 是否已经在另一台机器上 运行 我创建了另一个 TCP Socket 试图连接到相同的 IPEndPoint。如果连接失败,这意味着 Form1 没有其他地方是 运行,我可以安全地打开它。如果连接有效 Form1 已经在某处打开,我可以通知用户。