有没有办法检查文件是否正在使用中?

Is there a way to check if a file is in use?

提问人:Dawsy 提问时间:5/18/2009 最后编辑:Selim YildizDawsy 更新时间:4/10/2023 访问量:705314

问:

我正在用 C# 编写一个程序,需要重复访问 1 个图像文件。大多数情况下,它都可以工作,但是如果我的计算机运行速度很快,它将在文件保存回文件系统之前尝试访问该文件并引发错误:

“文件正在被另一个进程使用”

我想找到解决这个问题的方法,但我所有的谷歌搜索都只能通过使用异常处理来创建检查。这违背了我的宗教信仰,所以我想知道是否有人有更好的方法?

C# .NET -IO 文件 锁定

评论

34赞 BobbyShaftoe 5/18/2009
好的,您可以通过检查系统上所有打开的句柄来测试它。但是,由于 Windows 是一个多任务操作系统,因此在运行代码以确定文件是否打开并且您认为它未打开后,进程代码可能会立即开始使用该文件,然后在您尝试使用它时收到错误。但是,先检查并没有错;只是不要以为当你真正需要它时它没有使用。
6赞 BobbyShaftoe 5/18/2009
但只是为了这个具体问题;我建议不要检查文件句柄,只尝试一些预设次数,比如 3-5 次,然后失败。
39赞 Lee Louviere 3/11/2013
你的哲学对例外的理解很差。大多数人认为例外意味着神圣的废话,从厄运中走出来,有些东西是错误的,死了,死了。当异常意味着......例外。这意味着发生了一些您需要“处理”(或解释)的异常情况。也许你想继续重试数据访问,也许用户需要知道你无法获得连接。你是做什么工作的?您处理 ConnectionFailedException 并通知用户,因此,他们可能会在一小时后停止尝试,并注意到电缆已拔下。
2赞 webs 10/20/2018
Lee Louviere 的 op 对使用异常工作有一定的厌恶。如果您可以轻松使用 filexists 方法来了解文件是否存在,还存在哪些类似的命令来了解您要使用的文件是否正在使用中?事实上,我相信这是 op 真正要问的问题。
2赞 ChrisMercator 8/17/2020
如果我不想弄清楚我是否可以写入文件,但如果我宁愿不写,尽管我可以,因为其他人目前正在处理同一个文件怎么办?

答:

4赞 Luke Schafer 5/18/2009 #1

我所知道的唯一方法是使用 Win32 独占锁 API,它不是太快,但存在示例。

大多数人,对于一个简单的解决方案,只需尝试/捕捉/睡眠循环。

评论

3赞 Harry Johnston 9/8/2017
如果不先打开文件,则无法使用此 API,此时不再需要此 API。
4赞 TheLastGIS 2/18/2020
薛定谔的文件。
6赞 Karl Johan 5/18/2009 #2

也许您可以使用 FileSystemWatcher 并监视 Changed 事件。

我自己没有用过这个,但可能值得一试。如果在这种情况下文件系统观察器有点重,我会选择 try/catch/sleep 循环。

评论

1赞 Ben F 6/20/2012
不过,FileSystemWatcher 不能很好地同时处理大量更改,因此请小心。
1赞 devlord 4/22/2013
顺便说一句,你们有没有注意到在调试和观察线程时,MS 称他们自己的 FSW“FileSystemWather”?到底什么是水?
4赞 7/8/2011
使用 FileSystemWatcher 无济于事,因为 Created 和 Changed 事件在文件创建/更改开始时引发。即使是小文件,操作系统也需要比 .NET 应用程序通过 FileSystemEventHandler 回调运行所需的时间更长。这太可悲了,但除了估计访问文件之前的等待时间或遇到异常循环之外,别无选择......
3赞 freedeveloper 8/8/2018
确切地说,我遇到了这个问题,因为带有 FileSystemWatcher 的 Windows 服务在进程关闭文件之前尝试读取文件。
609赞 Spence 5/18/2009 #3

您可能会遇到线程争用条件,有记录在案的示例表明这被用作安全漏洞。如果检查文件是否可用,但随后尝试使用它,则可能会在此时抛出,恶意用户可能会利用该文件来强制和利用您的代码。

你最好的选择是尝试捕捉/最后,它试图获取文件句柄。

try
{
   using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
   {
        // File/Stream manipulating code here
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}

评论

149赞 Sedat Kapanoglu 1/4/2011
+1.没有 100% 安全的方法来“了解文件是否正在使用”,因为在执行检查几毫秒后,该文件可能不再使用,反之亦然。相反,您只需打开文件并在没有例外的情况下使用它。
10赞 TamusJRoyce 8/1/2011
太糟糕了,.NET 不支持 CAS。类似于返回成功/失败的 TryOpenFile(Ref FileHandle)。应该始终有一种解决方法,而不是仅仅依赖于异常处理。我想知道Microsoft Office是如何做到的。
3赞 Spence 8/2/2011
这里要理解的关键是,此 API 只是使用 Windows API 来获取文件句柄。因此,他们需要转换从 C API 收到的错误代码,并将其包装为要抛出的异常。我们在 .Net 中有异常处理,所以为什么不使用它。这样,就可以在代码中编写干净的转发路径,并将错误处理保留在单独的代码路径中。
41赞 Spence 11/28/2012
using 语句用于确保在我完成后关闭流。我想你会发现 using() {} 的字符数比 try {} finally { obj 少。Dispose() }。您还会发现,您现在需要在 using 语句之外声明您的对象引用,这更像是类型化。如果你有一个显式的接口,你也必须强制转换。最后,你希望尽快释放,最终逻辑可能具有 UI 或任何其他与调用 IDispose 无关的长时间运行的操作。</咆哮>
3赞 Spence 1/31/2013
这并不能否定这样一个事实,即您必须在 try 之外声明您的对象,并且必须显式调用 dispose,这 use 对您有用并且意味着同样的事情。
0赞 Carra 5/18/2009 #4

尝试将文件移动/复制到临时目录。如果可以的话,它没有锁,你可以安全地在临时目录中工作,而不会被锁住。否则,只需尝试在 x 秒内再次移动它。

评论

0赞 Cullub 8/8/2014
@jcolebrand锁是什么?你复制的那个?还是你放在临时目录中的那个?
5赞 jcolebrand 8/8/2014
如果您复制文件,期望没有其他人在处理它,并且您将使用临时文件,然后有人在您复制它后立即锁定它,那么您可能会丢失数据。
0赞 BitLauncher 4/14/2021
之前尝试过(将大文件从本地硬盘复制到虚拟服务器上的虚拟驱动器(1 分钟) - 尝试使用 File.Move() 尝试检测复制的结束 - 但它失败了!现在文件存在于两个目录中...最后它让它复制了 3 次......
674赞 ChrisW 6/2/2009 #5

更新了有关此解决方案的说明:对于只读文件,检查 with 将失败,因此已将解决方案修改为 check with 。FileAccess.ReadWriteFileAccess.Read

源语言:在过去的几年里,我一直在使用这段代码,我没有遇到任何问题。

理解你对使用异常的犹豫,但你不能一直避免它们:

protected virtual bool IsFileLocked(FileInfo file)
{
    try
    {
        using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }

    //file is not locked
    return false;
}

评论

69赞 adeel825 1/29/2010
这是一个很好的解决方案,但我有一条评论 - 您可能不想使用访问模式 FileAccess.Read 打开文件,因为如果文件恰好是只读的,则 ReadWrite 将始终失败。
241赞 Polyfun 5/26/2010
-1.这是一个糟糕的答案,因为在 IsFileLocked 中关闭文件后,在您的线程有机会打开它之前,该文件可能会被另一个线程/进程锁定。
21赞 Manuzor 7/4/2012
我认为这是一个很好的答案。我将其用作扩展方法public static bool IsLocked(this FileInfo file) {/*...*/}
57赞 Pierre Lebeaupin 3/11/2013
@ChrisW:你可能想知道这是怎么回事。不要惊慌。你只是受到Daily WTF社区的愤怒:thedailywtf.com/Comments/......
19赞 Lee Louviere 3/11/2013
@ChrisW 为什么这是一件坏事。这个社区在这里指出好的和坏的答案。如果一群专业人士注意到这是一件坏事,并加入投反对票,那么该网站就是 WAI。在你得到否定之前,如果你读了那篇文章,他们会说要“对正确的答案投赞成票”,而不是对错误的答案投反对票。您是否也希望他们在评论中解释他们的赞成票。谢谢你把我介绍给另一个好网站!
5赞 Julian 8/23/2011 #6
static bool FileInUse(string path) {
    try {
        using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate)) {
            return !fs.CanWrite;
        }
        //return false;
    }
    catch (IOException ex) {
        return true;
    }
}

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse = FileInUse(filePath);

// Then you can do some checking
if (isFileInUse)
   Console.WriteLine("File is in use");
else
   Console.WriteLine("File is not in use");

希望这有帮助!

评论

13赞 Lakey 3/26/2013
您执行的实际检查是可以的;把它放在函数中是误导性的。您不想在打开文件之前使用这样的函数。在函数内部,文件被打开、检查和关闭。然后程序员假设该文件仍然可以使用,并尝试打开它以供使用。这很糟糕,因为它可能被排队打开此文件的另一个进程使用和锁定。在第一次打开(用于检查)和第二次打开(用于使用)之间,操作系统可能已取消进程计划,并可能正在运行另一个进程。
-1赞 zzfima 1/17/2012 #7

我使用此解决方法,但是在使用 IsFileLocked 函数检查文件锁定和打开文件之间有一个时间跨度。在这个时间跨度内,其他一些线程可以打开该文件,所以我会得到 IOException。

所以,我为此添加了额外的代码。就我而言,我想要加载 XDocument:

        XDocument xDoc = null;

        while (xDoc == null)
        {
            while (IsFileBeingUsed(_interactionXMLPath))
            {
                Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
                Thread.Sleep(100);
            }
            try
            {
                xDoc = XDocument.Load(_interactionXMLPath);
            }
            catch
            {
                Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
            }
        }

你觉得怎么样?我可以改变一些东西吗?也许我根本不需要使用IsFileBeingUsed函数?

谢谢

评论

5赞 Kiquenet 7/30/2013
什么是IsFileBeingUsed?关于 IsFileBeingUsed 的源代码 ?
1赞 Boppity Bop 5/6/2021
IsFileBeingUsed - 来自已接受的答案。如果您最终想访问该文件,他只是提供了一种如何使用它的方法。这个答案没有错。
102赞 Jeremy Thompson 6/16/2012 #8

使用此选项来检查文件是否已锁定:

using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;

private static bool IsFileLocked(Exception exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}

internal static bool CanReadFile(string filePath)
{
    //Try-Catch so we dont crash the program and can check the exception
    try {
        //The "using" is important because FileStream implements IDisposable and
        //"using" will avoid a heap exhaustion situation when too many handles  
        //are left undisposed.
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
            if (fileStream != null) fileStream.Close();  //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
        }
    }
    catch (IOException ex) {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex)) {
            // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
            return false;
        }
    }
    finally
    { }
    return true;
}
}

出于性能原因,我建议您在同一操作中读取文件内容。以下是一些示例:

public static byte[] ReadFileBytes(string filePath)
{
    byte[] buffer = null;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;  // sum is a buffer offset for next reading

            fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }
    return buffer;
}

public static string ReadFileTextWithEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            //Depending on the encoding you wish to use - I'll leave that up to you
            fileContents = System.Text.Encoding.Default.GetString(buffer);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    { }     
    return fileContents;
}

public static string ReadFileTextNoEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) 
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            char[] chars = new char[buffer.Length / sizeof(char) + 1];
            System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
            fileContents = new string(chars);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }

    return fileContents;
}

自己试试吧:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");

评论

13赞 Kris 3/12/2013
如果那里没有那么多“神奇的数字”,我会投赞成票 en.wikipedia.org/wiki/Magic_number_(编程)
3赞 Kris 3/15/2013
我指的是 errorCode 比较,而不是位移。虽然现在你提到它......
1赞 Askolein 4/22/2013
您的 Catch 应该处于 ,而不是常规状态,然后对类型进行测试。IOExceptionException
3赞 Askolein 6/18/2013
@JeremyThompson可悲的是,你把具体的放在一般的之后。一般的会抓住一切经过的东西,而具体的人将永远是孤独的。只需交换两者即可。IOExceptionIOException
0赞 shindigo 4/19/2014
我喜欢这个解决方案。另一个建议:在 catch 中作为 if(IsFileLocked(ex)) 的 else 我会抛出 ex。然后,这将通过引发异常来处理文件不存在(或任何其他 IOException)的情况。
3赞 atlaste 9/2/2014 #9

根据我的经验,您通常希望这样做,然后“保护”您的文件以做一些花哨的事情,然后使用“受保护”文件。如果你只想像这样使用一个文件,你可以使用 Jeremy Thompson 在回答中解释的技巧。但是,如果您尝试在大量文件上执行此操作(例如,当您编写安装程序时),您将受到相当大的伤害。

一个非常优雅的解决方法是利用这样一个事实,即如果您的文件系统中的一个文件正在使用,则不允许您更改文件夹名称。将文件夹保存在同一个文件系统中,它会像魅力一样工作。

请注意,您应该了解可以利用的明显方式。毕竟,文件不会被锁定。此外,请注意,还有其他原因可能导致操作失败。显然,正确的错误处理 (MSDN) 可以在这方面提供帮助。Move

var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));

try
{
    Directory.Move(originalFolder, someFolder);

    // Use files
}
catch // TODO: proper exception handling
{
    // Inform user, take action
}
finally
{
    Directory.Move(someFolder, originalFolder);
}

对于单个文件,我会坚持 Jeremy Thompson 发布的锁定建议。

评论

0赞 Jeremy Thompson 11/21/2014
嗨,由于答案的顺序发生了变化,您能否澄清一下您对这个流行 QA 的读者的意思是上面的哪篇帖子。谢谢。
1赞 atlaste 11/21/2014
@JeremyThompson 你是对的,谢谢,我会编辑帖子。我会使用您的解决方案,主要是因为您正确使用和检查锁。FileShare
8赞 Jay Byford-Rew 5/26/2015 #10

只需按预期使用异常即可。接受文件正在使用中,然后重试,重复,直到操作完成。这也是最有效的,因为您不会浪费任何周期在行动之前检查状态。

例如,使用以下函数

TimeoutFileAction(() => { System.IO.File.etc...; return null; } );

2 秒后超时的可重用方法

private T TimeoutFileAction<T>(Func<T> func)
{
    var started = DateTime.UtcNow;
    while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
    {
        try
        {
            return func();                    
        }
        catch (System.IO.IOException exception)
        {
            //ignore, or log somewhere if you want to
        }
    }
    return default(T);
}
7赞 rboy 10/15/2015 #11

上面接受的答案会遇到一个问题,即如果已打开文件以使用 FileShare.Read 模式进行写入,或者如果文件具有只读属性,则代码将不起作用。这种修改后的解决方案工作最可靠,需要记住两件事(对于公认的解决方案也是如此):

  1. 它不适用于使用写入共享模式打开的文件
  2. 这没有考虑线程问题,因此您需要将其锁定或单独处理线程问题。

请记住上述内容,这将检查文件是被锁定以进行写入,还是被锁定以防止读取

public static bool FileLocked(string FileName)
{
    FileStream fs = null;

    try
    {
        // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
        fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
    }
    catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
    {
        // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
        try
        {
            fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (Exception)
        {
            return true; // This file has been locked, we can't even open it to read
        }
    }
    catch (Exception)
    {
        return true; // This file has been locked
    }
    finally
    {
        if (fs != null)
            fs.Close();
    }
    return false;
}

评论

0赞 Harry Johnston 10/17/2015
仍然有与接受的答案相同的问题 - 它只告诉您文件是否在某个特定时刻被另一个进程锁定,这不是有用的信息。当函数返回时,结果可能已经过时了!
2赞 rboy 10/17/2015
确实如此,只能在任何给定时刻进行检查(或订阅事件),与公认的解决方案相比,这种方法的优点是它可以检查只读属性和写锁,而不会返回误报。
7赞 Ivan Branets 10/20/2015 #12

您可以返回一个任务,该任务会在流可用时立即为您提供流。这是一个简化的解决方案,但它是一个很好的起点。它是线程安全的。

private async Task<Stream> GetStreamAsync()
{
    try
    {
        return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
    }
    catch (IOException)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await GetStreamAsync();
    }
}

您可以像往常一样使用此流:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}

评论

14赞 CAD bloke 7/24/2017
多少秒后堆栈会从递归中溢出?GetStreamAsync()
0赞 Ivan Branets 7/26/2017
@CADbloke,你提出了一个非常好的观点。事实上,我的样本可能有堆栈溢出异常,以防文件长时间不可用。与此答案相关的 stackoverflow.com/questions/4513438/...,它可能会在 5 小时内引发异常。
1赞 Ivan Branets 7/26/2017
与您的用例相关,如果假设 10 次读取文件的尝试失败,则最好抛出 I/O 异常。另一种策略可能是在 10 次尝试失败后增加一秒钟的等待时间。您也可以将两者混合使用。
0赞 CAD bloke 7/26/2017
我会(并且确实)只是提醒用户文件已锁定。他们通常自己锁定它,所以他们可能会对此做些什么。或不。
0赞 Ivan Branets 7/31/2017
在某些情况下,您需要使用重试策略,因为文件可能尚未准备就绪。想象一下,一个桌面应用程序将图像下载到某个临时文件夹中。应用程序开始下载,同时在文件资源管理器中打开此文件夹。Windows 希望立即创建缩略图并锁定文件。同时,您的应用会尝试将锁定的图像替换到其他位置。如果不使用重试策略,将收到异常。
5赞 cdiggins 8/23/2016 #13

以下是一些代码,据我所知,它们与公认的答案相同,但代码更少:

    public static bool IsFileLocked(string file)
    {
        try
        {
            using (var stream = File.OpenRead(file))
                return false;
        }
        catch (IOException)
        {
            return true;
        }        
    }

但是,我认为通过以下方式执行此操作会更强大:

    public static void TryToDoWithFileStream(string file, Action<FileStream> action, 
        int count, int msecTimeOut)
    {
        FileStream stream = null;
        for (var i = 0; i < count; ++i)
        {
            try
            {
                stream = File.OpenRead(file);
                break;
            }
            catch (IOException)
            {
                Thread.Sleep(msecTimeOut);
            }
        }
        action(stream);
    }
2赞 Tomasz Żmuda 9/7/2017 #14

您可以使用我的库从多个应用程序访问文件。

可以从 nuget 安装它:Install-Package Xabe.FileLock

如果您想了解有关它的更多信息,请查看 https://github.com/tomaszzmuda/Xabe.FileLock

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}

fileLock.Acquire 方法仅在可以锁定此对象独占的文件时才返回 true。 但是上传文件的应用程序也必须在文件锁定中执行此操作。 如果对象无法访问 metod 则返回 false。

评论

4赞 Harry Johnston 9/8/2017
需要使用该文件的所有进程进行协作。不太可能适用于 OP 的原始问题。
0赞 HackSlash 6/16/2022
应该在主声明之外。usingif
-1赞 Vinney Kelly 4/5/2018 #15

我很想知道这是否会触发任何 WTF 反射。我有一个流程,可以从控制台应用程序创建并随后启动PDF文档。但是,我正在处理一个弱点,即如果用户多次运行该进程,在不首先关闭先前生成的文件的情况下生成相同的文件,则应用程序将抛出异常并死机。这种情况相当频繁,因为文件名基于销售报价单编号。

我没有以这种不优雅的方式失败,而是决定依赖自动递增的文件版本控制:

private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
    try
    {
        var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
        using (var writer = new FileStream(filePath, FileMode.Create))
        {
            writer.Write(data, 0, data.Length);
        }
        return filePath;
    }
    catch (IOException)
    {
        return WriteFileToDisk(data, fileName, ++version);
    }
}

也许可以对块给予更多的关注,以确保我捕捉到正确的 IOException。我可能还会在启动时清除应用程序存储,因为这些文件无论如何都是临时的。catch

我意识到这超出了 OP 问题的范围,即简单地检查文件是否正在使用中,但这确实是我到达这里时想要解决的问题,所以也许它对其他人有用。

5赞 Bernhard 10/9/2018 #16

除了工作 3 行之外,仅供参考:如果您想要完整的信息 - Microsoft 开发人员中心上有一个小项目:

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

现在位于: https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs

从引言:

在 .NET Framework 4.0 中开发的 C# 示例代码将有助于 找出哪个进程对文件有锁定。包含在 rstrtmgr.dll 中的 RmStartSession 函数已 用于创建重启管理器会话,并根据 结果:创建 Win32Exception 对象的新实例。后 通过 RmRegisterRescources 函数将资源注册到重启管理器会话,调用 RmGetList 函数来检查 哪些应用程序正在通过枚举来使用特定文件 RM_PROCESS_INFO数组。

它通过连接到“重新启动管理器会话”来工作。

重启管理器使用向会话注册的资源列表 确定必须关闭并重新启动哪些应用程序和服务。资源可以通过文件名、服务短名称或 RM_UNIQUE_PROCESS描述正在运行的应用程序的结构。

对于您的特定需求来说,它可能有点过度设计...... 但是,如果这是想要的,请继续获取 vs-project。

评论

1赞 Boppity Bop 5/6/2021
如果它有一个示例代码,这可能是一个非常有趣的解决方案。您提供的链接现在无效。.
0赞 ttt 4/9/2019 #17

这样的事情会有帮助吗?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter("filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}
3赞 Benjamin Curtis Drake 8/16/2019 #18

我曾经需要将 PDF 上传到在线备份存档。但是,如果用户在另一个程序(如 PDF 阅读器)中打开文件,则备份将失败。在匆忙中,我尝试了这个线程中的一些顶级答案,但无法让它们起作用。对我有用的是尝试将 PDF 文件移动到它自己的目录中。我发现,如果文件在另一个程序中打开,这将失败,如果移动成功,则不需要还原操作,因为如果将其移动到单独的目录,则会失败。我想发布我的基本解决方案,以防它可能对其他人的特定用例有用。

string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
    fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
    open_elsewhere = true;
}

if (open_elsewhere)
{
    //handle case
}
-1赞 Ribaz 7/31/2020 #19
retry_possibility:
//somecode here

try
{
    using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
    {
        stream.Close();
    }
    //write or open your file here
}
catch (IOException)
{
    DialogResult dialogResult = MessageBox.Show("This file is opened by you or another user. Please close it and press retry.\n"+ expFilePath, "File Locked", MessageBoxButtons.RetryCancel);
    if (dialogResult == DialogResult.Retry)
    {
        goto retry_possibility;
    }
    else if (dialogResult == DialogResult.Cancel)
    {
        //do nothing
    }
}

评论

0赞 HackSlash 6/16/2022
不要在 C# 中使用。请解释您的代码的作用以及为什么使用您的代码。goto
13赞 asdf101 4/18/2021 #20

我最近遇到了这个问题,发现这个:https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors

在这里,Microsoft 描述了以下检查是否由于锁定文件而导致的方法:IOException

catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
    Console.WriteLine("There is a sharing violation.");
}

评论

1赞 Azelski 6/14/2021
到目前为止最好的答案。仅适用于锁定的文件,不适用于一般的 IO 异常。
0赞 Jeremy Thompson 4/14/2023
这只ERROR_SHARING_VIOLATION,请参阅我对ERROR_LOCK_VIOLATION的回答,这是导致此问题的更常见原因。
0赞 asdf101 4/15/2023
@JeremyThompson因为这是问题所问的,所以他们希望避免“文件正在被另一个进程使用”错误。如果您还想捕获锁定违规,您也可以简单地检查一下,恕我直言,您的答案过于复杂。(e.HResult & 0x0000FFFF) == 33