有没有什么方法可以不使用try/catch块来检查文件是否被锁定?

现在,我所知道的唯一方法是打开文件并捕获任何System.IO.IOException。


当前回答

您可以在感兴趣的文件区域上通过互操作调用LockFile。这将不会抛出异常,如果它成功,你将对文件的那一部分(由你的进程持有)拥有一个锁,这个锁将一直持有,直到你调用UnlockFile或你的进程死亡。

其他回答

不,不幸的是,如果你仔细想想,这些信息无论如何都是毫无价值的,因为文件可能在下一秒就被锁定(阅读:短时间跨度)。

为什么您特别需要知道文件是否被锁定?知道了这一点,我们可能会以其他方式给你一些好的建议。

如果你的代码是这样的:

if not locked then
    open and update file

然后,在这两行之间,另一个进程可以很容易地锁定文件,给您带来您一开始试图避免的相同问题:异常。

DixonD出色答案的一个变体(如上)。

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           TimeSpan timeout,
                           out Stream stream)
{
    var endTime = DateTime.Now + timeout;

    while (DateTime.Now < endTime)
    {
        if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
            return true;
    }

    stream = null;
    return false;
}

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           out Stream stream)
{
    try
    {
        stream = File.Open(path, fileMode, fileAccess, fileShare);
        return true;
    }
    catch (IOException e)
    {
        if (!FileIsLocked(e))
            throw;

        stream = null;
        return false;
    }
}

private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;

private static bool FileIsLocked(IOException ioException)
{
    var errorCode = (uint)Marshal.GetHRForException(ioException);
    return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}

用法:

private void Sample(string filePath)
{
    Stream stream = null;

    try
    {
        var timeOut = TimeSpan.FromSeconds(1);

        if (!TryOpen(filePath,
                     FileMode.Open,
                     FileAccess.ReadWrite,
                     FileShare.ReadWrite,
                     timeOut,
                     out stream))
            return;

        // Use stream...
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }
}

下面是DixonD代码的一个变体,它增加了等待文件解锁的秒数,并重试:

public bool IsFileLocked(string filePath, int secondsToWait)
{
    bool isLocked = true;
    int i = 0;

    while (isLocked &&  ((i < secondsToWait) || (secondsToWait == 0)))
    {
        try
        {
            using (File.Open(filePath, FileMode.Open)) { }
            return false;
        }
        catch (IOException e)
        {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            i++;

            if (secondsToWait !=0)
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
        }
    }

    return isLocked;
}


if (!IsFileLocked(file, 10))
{
    ...
}
else
{
    throw new Exception(...);
}

您可以在感兴趣的文件区域上通过互操作调用LockFile。这将不会抛出异常,如果它成功,你将对文件的那一部分(由你的进程持有)拥有一个锁,这个锁将一直持有,直到你调用UnlockFile或你的进程死亡。

在Powershell中也是一样

function Test-FileOpen
{
    Param
    ([string]$FileToOpen)
    try
    {
        $openFile =([system.io.file]::Open($FileToOpen,[system.io.filemode]::Open))
        $open =$true
        $openFile.close()
    }
    catch
    {
        $open = $false
    }
    $open
}