C# 如何检查文件锁定?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1304/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-07-31 12:24:01  来源:igfitidea点击:

How to check for file lock?

提问by ricree

Is there any way to check whether a file is locked without using a try/catch block?

有没有办法在不使用 try/catch 块的情况下检查文件是否被锁定?

Right now, the only way I know of is to just open the file and catch any System.IO.IOException.

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

采纳答案by Lasse V. Karlsen

No, unfortunately, and if you think about it, that information would be worthless anyway since the file could become locked the very next second (read: short timespan).

不,不幸的是,如果您考虑一下,该信息无论如何都将毫无价值,因为文件可能会在下一秒被锁定(阅读:短时间跨度)。

Why specifically do you need to know if the file is locked anyway? Knowing that might give us some other way of giving you good advice.

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

If your code would look like this:

如果您的代码如下所示:

if not locked then
    open and update file

Then between the two lines, another process could easily lock the file, giving you the same problem you were trying to avoid to begin with: exceptions.

然后在这两行之间,另一个进程可以轻松锁定文件,从而给您带来您试图避免的相同问题:异常。

回答by S?ren Kuklau

Then between the two lines, another process could easily lock the file, giving you the same problem you were trying to avoid to begin with: exceptions.

然后在这两行之间,另一个进程可以轻松锁定文件,从而给您带来您试图避免的相同问题:异常。

However, this way, you would know that the problem is temporary, and to retry later. (E.g., you could write a thread that, if encountering a lock while trying to write, keeps retrying until the lock is gone.)

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

The IOException, on the other hand, is not by itself specific enough that locking is the cause of the IO failure. There could be reasons that aren't temporary.

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

回答by Brian R. Bondy

You can see if the file is locked by trying to read or lock it yourself first.

您可以通过尝试先自己读取或锁定文件来查看文件是否被锁定。

Please see my answer here for more information.

请在此处查看我的回答以获取更多信息

回答by Sam Saffron

You could call LockFilevia interop on the region of file you are interested in. This will not throw an exception, if it succeeds you will have a lock on that portion of the file (which is held by your process), that lock will be held until you call UnlockFileor your process dies.

您可以通过互操作在您感兴趣的文件区域上调用LockFile。这不会引发异常,如果成功,您将对文件的该部分(由您的进程持有)锁定,该锁定将是一直保持到您调用UnlockFile或您的进程终止

回答by Sergio Vicente

Instead of using interop you can use the .NET FileStream class methods Lock and Unlock:

您可以使用 .NET FileStream 类方法 Lock 和 Unlock,而不是使用互操作:

FileStream.Lock http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx

FileStream.Lock http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx

FileStream.Unlock http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx

FileStream.Unlock http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx

回答by DixonD

When I faced with a similar problem, I finished with the following code:

当我遇到类似的问题时,我完成了以下代码:

public bool IsFileLocked(string filePath)
{
    try
    {
        using (File.Open(filePath, FileMode.Open)){}
    }
    catch (IOException e)
    {
        var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);

        return errorCode == 32 || errorCode == 33;
    }

    return false;
}

回答by Aralmo

You can also check if any process is using this file and show a list of programs you must close to continue like an installer does.

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

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

回答by Tristan

A variation of DixonD's excellent answer (above).

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

Usage:

用法:

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

回答by live-love

Here's a variation of DixonD's code that adds number of seconds to wait for file to unlock, and try again:

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

回答by Bart Calixto

What I ended up doing is:

我最终做的是:

internal void LoadExternalData() {
    FileStream file;

    if (TryOpenRead("filepath/filename", 5, out file)) {
        using (file)
        using (StreamReader reader = new StreamReader(file)) {
         // do something 
        }
    }
}


internal bool TryOpenRead(string path, int timeout, out FileStream file) {
    bool isLocked = true;
    bool condition = true;

    do {
        try {
            file = File.OpenRead(path);
            return true;
        }
        catch (IOException e) {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            condition = (isLocked && timeout > 0);

            if (condition) {
                // we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
                timeout--;
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
            }
        }
    }
    while (condition);

    file = null;
    return false;
}