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

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


当前回答

不使用互操作,你可以使用。net FileStream类方法Lock和Unlock:

文件流。锁 http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx

文件流。解锁 http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx

其他回答

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

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

如果你的代码是这样的:

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();
    }
}

您还可以检查是否有任何进程正在使用此文件,并像安装程序一样显示必须关闭才能继续的程序列表。

public static string GetFileProcessName(string filePath)
{
    Process[] procs = Process.GetProcesses();
    string fileName = Path.GetFileName(filePath);

    foreach (Process proc in procs)
    {
        if (proc.MainWindowHandle != new IntPtr(0) && !proc.HasExited)
        {
            ProcessModule[] arr = new ProcessModule[proc.Modules.Count];

            foreach (ProcessModule pm in proc.Modules)
            {
                if (pm.ModuleName == fileName)
                    return proc.ProcessName;
            }
        }
    }

    return null;
}

下面是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(...);
}

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

但是,通过这种方式,您将知道问题是暂时的,并稍后重试。(例如,您可以编写一个线程,如果在尝试写入时遇到锁,则会不断重试,直到锁消失。)

另一方面,IOException本身还不够特定,以至于锁定是导致IO失败的原因。可能有一些不是暂时的原因。