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

发布于 2024-07-22 05:56:04 字数 226 浏览 6 评论 0原文

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

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

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

I'm writing a program in C# that needs to repeatedly access 1 image file. Most of the time it works, but if my computer's running fast, it will try to access the file before it's been saved back to the filesystem and throw an error:

"File in use by another process"

I would like to find a way around this, but all my Googling has only yielded creating checks by using exception handling. This is against my religion, so I was wondering if anyone has a better way of doing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

别理我 2024-07-29 05:56:04

更新了此解决方案的注释:对于只读文件,使用 FileAccess.ReadWrite 检查将会失败,因此该解决方案已修改为使用 FileAccess.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;
}

Updated NOTE on this solution: Checking with FileAccess.ReadWrite will fail for Read-Only files so the solution has been modified to check with FileAccess.Read.

ORIGINAL:
I've used this code for the past several years, and I haven't had any issues with it.

Understand your hesitation about using exceptions, but you can't avoid them all of the time:

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;
}
ま柒月 2024-07-29 05:56:04

您可能会遇到线程竞争状况,有记录的示例将其用作安全漏洞。 如果您检查该文件是否可用,但随后尝试使用它,您可能会在此时抛出该文件,恶意用户可能会利用该文件在您的代码中强制和利用。

最好的选择是 try catch / finally 尝试获取文件句柄。

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

You can suffer from a thread race condition on this which there are documented examples of this being used as a security vulnerability. If you check that the file is available, but then try and use it you could throw at that point, which a malicious user could use to force and exploit in your code.

Your best bet is a try catch / finally which tries to get the file handle.

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.
}
微凉徒眸意 2024-07-29 05:56:04

使用它来检查文件是否被锁定:

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");

Use this to check if a file is locked:

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

For performance reasons I recommend you read the file content in the same operation. Here are some examples:

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

Try it out yourself:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
我为君王 2024-07-29 05:56:04

我最近遇到了这个问题并发现了这个: 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.");
}

I recently came across this issue and found this: https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors.

Here, Microsoft describes the following method for checking if an IOException was due to a locked file:

catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
    Console.WriteLine("There is a sharing violation.");
}
爺獨霸怡葒院 2024-07-29 05:56:04

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

使用下面的函数,例如

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

Just use the exception as intended. Accept that the file is in use and try again, repeatedly until your action is completed. This is also the most efficient because you do not waste any cycles checking the state before acting.

Use the function below, for example

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

Reusable method that times out after 2 seconds

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);
}
妄断弥空 2024-07-29 05:56:04

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

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

You can return a task which gives you a stream as soon as it becomes available. It's a simplified solution, but it is a good starting point. It's thread safe.

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

You can use this stream as usual:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
彼岸花似海 2024-07-29 05:56:04

上面接受的答案存在一个问题,如果文件已打开并使用 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;
}

The accepted answers above suffer an issue where if file has been opened for writing with a FileShare.Read mode or if the file has a Read-Only attribute the code will not work. This modified solution works most reliably, with two things to keep in mind (as true for the accepted solution also):

  1. It will not work for files that has been opened with a write share mode
  2. This does not take into account threading issues so you will need to lock it down or handle threading issues separately.

Keeping the above in mind, this checks if the file is either locked for writing or locked to prevent reading:

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;
}
月野兔 2024-07-29 05:56:04

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

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

Perhaps you could use a FileSystemWatcher and watch for the Changed event.

I haven't used this myself, but it might be worth a shot. If the filesystemwatcher turns out to be a bit heavy for this case, I would go for the try/catch/sleep loop.

扬花落满肩 2024-07-29 05:56:04

据我所知,这里有一些代码与接受的答案具有相同的功能,但代码较少:

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

Here is some code that as far as I can best tell does the same thing as the accepted answer but with less code:

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

However I think it is more robust to do it in the following manner:

    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);
    }
丢了幸福的猪 2024-07-29 05:56:04

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

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

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

来自简介:

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

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

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

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

Aside from working 3-liners and just for reference: If you want the full blown information - there is a little project on Microsoft Dev Center:

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

Now found at:
https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs

From the Introduction:

The C# sample code developed in .NET Framework 4.0 would help in
finding out which is the process that is having a lock on a file.
RmStartSession function which is included in rstrtmgr.dll has been
used to create a restart manager session and according to the return
result a new instance of Win32Exception object is created. After
registering the resources to a Restart Manager session via
RmRegisterRescources function, RmGetList function is invoked to check
what are the applications are using a particular file by enumerating
the RM_PROCESS_INFO array.

It works by connecting to the "Restart Manager Session".

The Restart Manager uses the list of resources registered with the session to
determine which applications and services must be shut down and restarted.
Resources can be identified by filenames, service short names, or
RM_UNIQUE_PROCESS structures
that describe running applications.

It might be a little overengineered for your particular needs...
But if that is what you want, go ahead and grab the vs-project.

初见你 2024-07-29 05:56:04
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");

希望这可以帮助!

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");

Hope this helps!

表情可笑 2024-07-29 05:56:04

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

大多数人,为了解决这个问题,只需尝试/捕获/睡眠循环即可。

the only way I know of is to use the Win32 exclusive lock API which isn't too speedy, but examples exist.

Most people, for a simple solution to this, simply to try/catch/sleep loops.

凉城已无爱 2024-07-29 05:56:04

根据我的经验,您通常想要执行此操作,然后“保护”您的文件以执行一些奇特的操作,然后使用“受保护”的文件。 如果您只想像这样使用一个文件,则可以使用 Jeremy Thompson 的答案中解释的技巧。 但是,如果您尝试对大量文件执行此操作(例如,当您编写安装程序时),您就会受到很大的伤害。

解决此问题的一种非常优雅的方法是利用以下事实:如果正在使用某个文件,您的文件系统将不允许您更改文件夹名称。 将文件夹保留在同一个文件系统中,它会像魅力一样工作。

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

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 发布的锁定建议。

In my experience, you usually want to do this, then 'protect' your files to do something fancy and then use the 'protected' files. If you have just one file you want to use like this, you can use the trick that's explained in the answer by Jeremy Thompson. However, if you attempt to do this on lots of files (say, for example when you're writing an installer), you're in for quite a bit of hurt.

A very elegant way this can be solved is by using the fact that your file system will not allow you to change a folder name if one of the files there it's being used. Keep the folder in the same file system and it'll work like a charm.

Do note that you should be aware of the obvious ways this can be exploited. After all, the files won't be locked. Also, be aware that there are other reasons that can result in your Move operation to fail. Obviously proper error handling (MSDN) can help out here.

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

For individual files I'd stick with the locking suggestion posted by Jeremy Thompson.

一个人练习一个人 2024-07-29 05:56:04

我曾经需要将 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
}

I once needed to upload PDFs to an online backup archive. But the backup would fail if the user had the file open in another program (such as PDF reader). In my haste, I attempted a few of the top answers in this thread but could not get them to work. What did work for me was trying to move the PDF file to its own directory. I found that this would fail if the file was open in another program, and if the move were successful there would be no restore-operation required as there would be if it were moved to a separate directory. I want to post my basic solution in case it may be useful for others' specific use cases.

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
}
风尘浪孓 2024-07-29 05:56:04

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

您可以从 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仅当可以锁定该对象独占的文件时。
但上传文件的应用程序也必须在文件锁定下进行。
如果对象不可访问,则方法返回 false。

You can use my library for accessing files from multiple apps.

You can install it from nuget: Install-Package Xabe.FileLock

If you want more information about it check
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 method will return true only if can lock file exclusive for this object.
But app which uploading file must do it in file lock too.
If object is inaccessible metod returns false.

佞臣 2024-07-29 05:56:04

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

Try and move/copy the file to a temp dir. If you can, it has no lock and you can safely work in the temp dir without getting locks. Else just try to move it again in x seconds.

素手挽清风 2024-07-29 05:56:04

这样的事情会有帮助吗?

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)
    {

    }
}

Would something like this help?

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)
    {

    }
}
静赏你的温柔 2024-07-29 05:56:04

我使用此解决方法,但在使用 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 函数?

谢谢

I use this workaround, but i have a timespan between when i check the file locking with IsFileLocked function and when i open the file. In this timespan some other thread can open the file, so i will get IOException.

So, i added extra code for this. In my case i want load 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!!!!!");
            }
        }

What do you think? Can i change some thing? Maybe i did not have to use IsFileBeingUsed function at all?

Thanks

装迷糊 2024-07-29 05:56:04

我很想知道这是否会引发任何 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);
    }
}

也许可以更加小心 catch 块,以确保我捕获正确的 IOException(s) 。 我可能还会在启动时清除应用程序存储,因为这些文件无论如何都是临时的。

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

I'm interested to see if this triggers any WTF reflexes. I have a process which creates and subsequently launches a PDF document from a console app. However, I was dealing with a frailty where if the user were to run the process multiple times, generating the same file without first closing the previously generated file, the app would throw an exception and die. This was a rather frequent occurrence because file names are based on sales quote numbers.

Rather than failing in such an ungraceful manner, I decided to rely on auto-incremented file versioning:

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

Probably some more care can be given to the catch block to ensure I'm catching the correct IOException(s). I'll probably also clear out the app storage on startup since these files are intended to be temporary anyways.

I realize this goes beyond the scope of the OP's question of simply checking if the file is in use but this was indeed the problem I was looking to solve when I arrived here so perhaps it will be useful to someone else.

夏末染殇 2024-07-29 05:56:04
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
    }
}
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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文