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

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

有帮助吗?

解决方案

这是该解决方案的更新注意:与FileAccess.ReadWrite检查因此该解决方案已被修改为检查与FileAccess.Read会为只读文件失败。虽然这种解决方案可行的,因为试图与FileAccess.Read来检查文件是否有写或读取它上面的锁会失败,但是,这种解决方案将不会如果文件没有在它写或读锁,工作即它已经打开(读或写)与FileShare.Read或FileShare.Write访问。

<强> ORIGINAL: 我用这个代码在过去的几年中,我还没有与它有任何问题。

了解您使用异常犹豫,但你不能避免他们所有的时间:

protected virtual bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
    }
    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;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

其他提示

可以从在此线程竞争条件,其有记载的这样的例子被用作安全漏洞受到影响。如果您检查该文件是可用的,但后来尝试使用它,你可以在这一点上,该恶意用户可能会使用武力和在你的代码利用抛出。

您最好的选择是一个尝试catch /终于它试图获取文件句柄。

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

使用此,以检查是否一个文件被锁定:

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

也许你可以使用 FileSystemWatcher的和手表用于改变的事件。

我没有这个使用自己,但它可能是值得一试。如果FileSystemWatcher的原来是有点重了这种情况下,我会去为的try / catch /睡眠循环。

只要使用例外如预期。接受该文件正在使用,然后再试一次,直至你的动作完成。这也是最有效的,因为你没有行动之前浪费任何周期检查状态。

使用下面的函数,例如

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

我知道的唯一途径是使用Win32独占锁API这是不是太迅速,但它的实例存在。

大多数人,对于一个简单的解决这个,只是为了的try / catch /睡眠循环。

static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            return true;
        }
    }

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

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

希望这有助于!

您可以返回它,只要它可用给你一个数据流任务。这是一个简化的解决方案,但它是一个很好的起点。这是线程安全的。

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

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

除了工作3派,只是供参考:如果你想在完全成熟信息 - 有微软开发中心一个小项目:

https://code.msdn.microsoft的.com / windowsapps /如何对专门的进程-704839f4

从简介:

  

在.NET框架开发的C#代码示例4.0将有助于   找出哪些是具有在文件的锁定的过程。   的 RmStartSession 它包括在rstrtmgr.dll功能已经   用于创建重启管理器会话,并根据返回   导致Win32Exception对象的新实例被创建。后   通过注册资源的重启管理器会话   的 RmRegisterRescources 功能,的 RmGetList 函数被调用,以检查   都有哪些应用程序正在使用一个特定的文件通过枚举   在 RM_PROCESS_INFO 阵列。

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

  

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

这可能是一个小的过度设计的为您的特殊需求。 但是,如果这是想,继续抢VS-项目。

下面是一些代码,据我可以告诉最好做同样的事情作为公认的答案,但用更少的代码:

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

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

可以从的NuGet安装:安装-封装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。

在我的经验,通常要做到这一点,那就是“保护”你的文件做一些花哨的,然后使用“保护”的文件。如果你要使用像这样只有一个文件,你可以使用在由杰里米·汤普森的答案解释的伎俩。然而,如果你试图做这在大量的文件(比如,例如当你写一个安装程序),你是在相当多的伤害。

这是可以解决的一个非常优雅的方式是使用的事实,你的文件系统将不会允许您更改文件夹名称,如果它正在使用的文件之一。保持文件夹中的同一个文件系统,它会工作就像一个魅力。

请注意,你应该知道的明显的方式可被利用。毕竟,这些文件不会被锁定。此外,要知道,有可能导致您的Move操作失败等原因。显然,正确的错误处理(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);
}

有关单个文件我会坚持用张贴杰里米·汤普森锁定建议。

我有兴趣看看这个触发任何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)。我大概也清除出应用程序存储在启动时,因为这些文件的目的是暂时的反正。

我明白,这超越了简单检查,如果该文件正在使用,但是这的确是我一直在寻找解决的时候我来到这里也许这将是别人有用问题的任择议定书的问题的范围。

会是这样的帮助呢?

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

    }
}

有一次,我需要的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
}

尝试和移动/将文件复制到一个临时目录。如果可以的话,它没有锁,你可以在临时目录安全工作没有得到锁。否则,只是尽量在x秒后再次移动它。

我使用此解决方案,但是我有当我检查该文件以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功能呢?

由于

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top