有没有更好的方法来确定文件是否由.NET中的另一个应用程序打开?

时间:2009-07-08 17:53:11

标签: c# .net file-io

我正在为日志做存档服务。由于某些文件是打开的并且正在使用中,因此我不能将它们归档,直到它们不再使用为止。我可以弄清楚如何查看该文件是否被另一个进程打开的唯一方法是下面的代码。

讨厌 代码必须有更好的方法。还有更好的方法吗?

try
{
    using (FileStream stream = File.Open("SomeFile.txt", FileMode.Open, FileAccess.ReadWrite))
    {
        // Do nothing just seeing if I could open it.
        return true;
    }
}
catch (IOException)
{
    // Exception so File is in use and can't open
    return false;
}

编辑:请注意,我不想抓住异常。我宁愿完全避免这个例外。我宁愿有一些神奇的函数,例如IsFileInUser(string fileName),它会返回一个布尔值而不会在下面捕获异常。

我看到的方法是使用OpenFile of PInvoke

5 个答案:

答案 0 :(得分:6)

这是我想出的。

首先,这是通过例外来做这件事的坏方法。

public static bool IsFileInUseBadMethod(string filePath)
{
    if (!File.Exists(filePath))
    {
        return false;
    }

    try
    {
        using (StreamReader reader = new StreamReader(File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
        {
            return false;
        }
    }
    catch (IOException)
    {
        return true;
    }
}

在我的机器上,当文件被使用时,这种方法需要大约1000万个滴答。

另一种方法是使用kernel32.dll

// Need to declare the function from the kernel32
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern Microsoft.Win32.SafeHandles.SafeFileHandle CreateFile(string lpFileName, System.UInt32 dwDesiredAccess, System.UInt32 dwShareMode, IntPtr pSecurityAttributes, System.UInt32 dwCreationDisposition, System.UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile);

private static readonly uint GENERIC_WRITE = 0x40000000;
private static readonly uint OPEN_EXISTING = 3;

这是函数

public static bool IsFileInUse(string filePath)
{
    if (!File.Exists(filePath))
    {
        return false;
    }

    SafeHandle handleValue = null;

    try
    {
        handleValue = FileHelper.CreateFile(filePath, FileHelper.GENERIC_WRITE, 0, IntPtr.Zero, FileHelper.OPEN_EXISTING, 0, IntPtr.Zero);

        bool inUse = handleValue.IsInvalid;

        return inUse;
    }
    finally
    {
        if (handleValue != null)
        {
            handleValue.Close();

            handleValue.Dispose();

            handleValue = null;
        }
    }
}

这大约有180万个蜱虫。 来自异常方法的超过800万个差异

然而,当文件没有被使用意味着没有抛出异常时,“BAD”方法的速度是130万,而kernel32.dll是200万。大约800k刻度更好。因此,如果我知道该文件在1/10的时间内不会被使用,那么使用Exception方法会更好。但有一次,与kernel32.dll相比,这是一个非常昂贵的滴答操作。

答案 1 :(得分:3)

我在代码示例中唯一要改进的是捕获File.Open抛出的一些特定异常。您至少需要捕获IOException和UnauthorizedAccessException。

通过输入catch,您可以捕获可能引发的任何异常,这是不好的做法。

答案 2 :(得分:1)

这个问题之前已经出现过了。搜索文件锁定/文件锁定。总结答案:不,没有更好的可靠方式。

理论上,您可以使用Windows API查找文件的开放IO句柄,但即使您看到该文件未被锁定,并继续使用您的逻辑,相同的文件也可能在您尝试之前再次被锁定访问它:

if FileIsNotLocked(file) {
   ...                         <=== file can get re-locked here!
   do something
}

答案 3 :(得分:1)

为了避免zvolkov提到的问题,如果您不介意在进行归档时锁定其他应用程序,那么您可以尝试使用锁定来打开文件,该文件会拒绝作者而不是读者。然后继续归档文件。当然这会锁定任何试图打开文件进行更新的应用程序 - 但鉴于你想在某个时候归档文件,没有什么好办法摆脱困境。

using (FileStream stream = new FileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
   // Archive your log.
}

答案 4 :(得分:0)

我认为你必须抓住异常。我只想把阅读和测试合二为一,也许就是这样。

bool success = false;

while( !success )
{
  try
  {
    //TODO: Try to open the file and write to it
    success = true;
  }
  catch( IOException ex )
  {
     // File in use, wait a little bit and try again.
     Thread.Sleep(xxx);
  }
}