C# 有没有办法检查文件是否正在使用?
我正在用C语言编写一个程序,需要反复访问一个图像文件。大多数情况下它都能工作,但如果我的计算机运行得很快,它会在文件被保存回文件系统之前尝试访问该文件并抛出错误: “另一进程正在使用的文件”C# 有没有办法检查文件是否正在使用?,c#,.net,file,file-io,file-locking,C#,.net,File,File Io,File Locking,我正在用C语言编写一个程序,需要反复访问一个图像文件。大多数情况下它都能工作,但如果我的计算机运行得很快,它会在文件被保存回文件系统之前尝试访问该文件并抛出错误: “另一进程正在使用的文件” 我想找到一种方法来解决这个问题,但我所有的谷歌搜索都只能通过使用异常处理来创建检查。这与我的宗教信仰背道而驰,所以我想知道是否有人有更好的方法来做这件事?我知道的唯一方法是使用Win32独占锁API,它速度不太快,但有一些例子 大多数人,为了简单地解决这个问题,只需尝试/catch/sleep循环。也许你可
我想找到一种方法来解决这个问题,但我所有的谷歌搜索都只能通过使用异常处理来创建检查。这与我的宗教信仰背道而驰,所以我想知道是否有人有更好的方法来做这件事?我知道的唯一方法是使用Win32独占锁API,它速度不太快,但有一些例子 大多数人,为了简单地解决这个问题,只需尝试/catch/sleep循环。也许你可以使用一个新的方法来观察更改的事件
我自己没用过这个,但可能值得一试。如果filesystemwatcher在这种情况下有点沉重,我会选择try/catch/sleep循环。在这种情况下,您可能会遇到线程争用情况,有文档记录的例子表明这种情况被用作安全漏洞。如果您检查该文件是否可用,然后尝试使用它,您可能会在该点抛出该文件,恶意用户可能会利用该文件在代码中强制和利用该文件 您最好的选择是尝试获取文件句柄的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.
}
尝试将文件移动/复制到临时目录。如果可以,它没有锁,您可以安全地在temp dir中工作,而无需获得锁。否则,请在x秒内再次尝试移动它。更新了此解决方案的说明:检查
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;
}
希望这有帮助 我使用此解决方法,但在使用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函数
谢谢使用此选项检查文件是否已锁定:
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;
}
}
你自己试试看:
byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
根据我的经验,您通常希望这样做,然后“保护”您的文件来做一些有趣的事情,然后使用“保护”文件。如果您只有一个文件想要像这样使用,您可以使用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发布的锁定建议。只需按预期使用异常即可。接受该文件正在使用,然后重试,直到操作完成。这也是最有效的,因为在执行操作之前不会浪费任何周期来检查状态
例如,使用下面的函数
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);
}
private T TimeoutFileAction(Func Func)
{
var start=DateTime.UtcNow;
while((DateTime.UtcNow-started).total毫秒<2000)
{
尝试
{
返回func();
}
捕获(System.IO.IOException异常)
{
//忽略,或者如果你想登录的话
}
}
返回默认值(T);
}
上述公认的答案存在一个问题,即如果文件已使用FileShare.Read模式打开以进行写入,或者如果文件具有只读属性,则代码将无法工作。此修改后的解决方案工作最可靠,需要记住两件事(对于已接受的解决方案也是如此):
它不适用于以写共享模式打开的文件
这不考虑线程问题,因此您需要将其锁定或单独处理线程问题
请记住上述内容,这将检查文件是锁定写入还是锁定防止读取:
您可以返回一个任务,该任务在可用时立即为您提供一个流。这是一个简化的解决方案,但它是一个很好的起点。它是线程安全的
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();
}
}
以下是一些代码,据我所知,它们与公认的答案具有相同的功能,但代码较少:
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);
}
public static void TryToDoWithFileStream(字符串文件、操作、,
整数计数,整数msecTimeOut)
{
FileStream=null;
对于(变量i=0;i
您可以使用我的库从多个应用程序访问文件
您可以从nuget:install包Xabe.FileLock安装它
如果您想了解更多信息,请检查
仅当可以锁定此对象的独占文件时,fileLock.Acquire方法才会返回true。
但是app-wh
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);
}
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);
}
ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
using(fileLock)
{
// file operations here
}
}
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);
}
}
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)
{
}
}
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
}
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
}
}