我正在用c#写一个程序,需要反复访问1个图像文件。大多数时候它是有效的,但如果我的计算机运行得很快,它会在文件保存回文件系统之前尝试访问该文件,并抛出一个错误:
文件正在被另一个进程使用
我想找到一个解决这个问题的方法,但我在谷歌上的所有搜索都只能通过使用异常处理来创建检查。这违背了我的宗教信仰,所以我想知道有没有更好的方法?
我正在用c#写一个程序,需要反复访问1个图像文件。大多数时候它是有效的,但如果我的计算机运行得很快,它会在文件保存回文件系统之前尝试访问该文件,并抛出一个错误:
文件正在被另一个进程使用
我想找到一个解决这个问题的方法,但我在谷歌上的所有搜索都只能通过使用异常处理来创建检查。这违背了我的宗教信仰,所以我想知道有没有更好的方法?
当前回答
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");
希望这能有所帮助!
其他回答
除了工作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吧。
尝试将文件移动/复制到临时目录。如果可以的话,它没有锁,您可以安全地在临时目录中工作而不需要锁。否则就在x秒内再移动一次。
使用此命令检查文件是否被锁定:
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");
根据我的经验,你通常想这样做,然后“保护”你的文件做一些奇特的事情,然后使用“保护”文件。如果你只想这样使用一个文件,你可以使用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发布的锁定建议。
更新了该解决方案的说明:使用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;
}