我正在用c#写一个程序,需要反复访问1个图像文件。大多数时候它是有效的,但如果我的计算机运行得很快,它会在文件保存回文件系统之前尝试访问该文件,并抛出一个错误:

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

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


当前回答

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

解决这个问题的一个非常优雅的方法是,如果您的文件系统中有一个文件正在使用,那么它将不允许您更改文件夹名称。将文件夹保存在同一个文件系统中,它会非常有效。

请注意,您应该了解可以利用这一点的明显方式。毕竟,文件不会被锁定。此外,请注意还有其他原因可能导致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);
}

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

其他回答

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

最好的方法是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.
}
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
    }
}
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");

希望这能有所帮助!

也许您可以使用FileSystemWatcher并观察“已更改”事件。

我自己还没用过这个,但也许值得一试。如果对于这种情况,文件系统监控器显得有点重,那么我将使用try/catch/sleep循环。

除了工作3-liner,只是为了参考:如果你想要完整的信息-在微软开发中心有一个小项目:

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

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

引言:

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.

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

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

为了满足你的特殊需求,它可能有点过度设计了…… 但如果这是你想要的,那就去拿vs-project吧。