我正在用c#写一个程序,需要反复访问1个图像文件。大多数时候它是有效的,但如果我的计算机运行得很快,它会在文件保存回文件系统之前尝试访问该文件,并抛出一个错误:
文件正在被另一个进程使用
我想找到一个解决这个问题的方法,但我在谷歌上的所有搜索都只能通过使用异常处理来创建检查。这违背了我的宗教信仰,所以我想知道有没有更好的方法?
我正在用c#写一个程序,需要反复访问1个图像文件。大多数时候它是有效的,但如果我的计算机运行得很快,它会在文件保存回文件系统之前尝试访问该文件,并抛出一个错误:
文件正在被另一个进程使用
我想找到一个解决这个问题的方法,但我在谷歌上的所有搜索都只能通过使用异常处理来创建检查。这违背了我的宗教信仰,所以我想知道有没有更好的方法?
当前回答
上面接受的答案遇到了一个问题,如果文件已被打开以使用FileShare写入。读取模式或如果文件具有只读属性,代码将无法工作。这个修改后的解决方案最可靠,需要记住两件事(对于公认的解决方案也是如此):
它将不适用于已以写共享模式打开的文件 这没有考虑到线程问题,所以你需要锁定它或单独处理线程问题。
记住上面的内容,这将检查文件是被写入锁定还是被阻止读取锁定:
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;
}
其他回答
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");
希望这能有所帮助!
我所知道的唯一方法是使用Win32独占锁API,这不是太快,但例子存在。
对于大多数人来说,一个简单的解决方案就是尝试/捕捉/睡眠循环。
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
}
}
我很有兴趣看看这是否会引发任何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)。我可能还会在启动时清除应用程序存储,因为这些文件是临时的。
我意识到这超出了OP的问题范围,即简单地检查文件是否在使用,但这确实是我到达这里时希望解决的问题,所以也许它对其他人有用。
我最近遇到了这个问题,找到了这个:https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors。
在这里,微软描述了以下检查IOException是否由于锁定文件导致的方法:
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
Console.WriteLine("There is a sharing violation.");
}