Related
I'm writing a program in C# that needs to repeatedly access 1 image file. Most of the time it works, but if my computer's running fast, it will try to access the file before it's been saved back to the filesystem and throw an error:
"File in use by another process"
I would like to find a way around this, but all my Googling has only yielded creating checks by using exception handling. This is against my religion, so I was wondering if anyone has a better way of doing it?
Updated NOTE on this solution: Checking with FileAccess.ReadWrite will fail for Read-Only files so the solution has been modified to check with FileAccess.Read.
ORIGINAL:
I've used this code for the past several years, and I haven't had any issues with it.
Understand your hesitation about using exceptions, but you can't avoid them all of the time:
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;
}
You can suffer from a thread race condition on this which there are documented examples of this being used as a security vulnerability. If you check that the file is available, but then try and use it you could throw at that point, which a malicious user could use to force and exploit in your code.
Your best bet is a try catch / finally which tries to get the file handle.
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.
}
Use this to check if a file is locked:
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;
}
}
For performance reasons I recommend you read the file content in the same operation. Here are some examples:
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;
}
Try it out yourself:
byte[] output1 = Helper.ReadFileBytes(#"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(#"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(#"c:\temp\test.txt");
I recently came across this issue and found this: https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors.
Here, Microsoft describes the following method for checking if an IOException was due to a locked file:
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
Console.WriteLine("There is a sharing violation.");
}
Just use the exception as intended. Accept that the file is in use and try again, repeatedly until your action is completed. This is also the most efficient because you do not waste any cycles checking the state before acting.
Use the function below, for example
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
Reusable method that times out after 2 seconds
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);
}
Perhaps you could use a FileSystemWatcher and watch for the Changed event.
I haven't used this myself, but it might be worth a shot. If the filesystemwatcher turns out to be a bit heavy for this case, I would go for the try/catch/sleep loop.
The accepted answers above suffer an issue where if file has been opened for writing with a FileShare.Read mode or if the file has a Read-Only attribute the code will not work. This modified solution works most reliably, with two things to keep in mind (as true for the accepted solution also):
It will not work for files that has been opened with a write share mode
This does not take into account threading issues so you will need to lock it down or handle threading issues separately.
Keeping the above in mind, this checks if the file is either locked for writing or locked to prevent reading:
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;
}
You can return a task which gives you a stream as soon as it becomes available. It's a simplified solution, but it is a good starting point. It's thread safe.
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();
}
}
You can use this stream as usual:
using (var stream = await FileStreamGetter.GetStreamAsync())
{
Console.WriteLine(stream.Length);
}
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");
Hope this helps!
Aside from working 3-liners and just for reference: If you want the full blown information - there is a little project on Microsoft Dev Center:
https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4
Now found at:
https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs
From the Introduction:
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.
It works by connecting to the "Restart Manager Session".
The Restart Manager uses the list of resources registered with the session to
determine which applications and services must be shut down and restarted.
Resources can be identified by filenames, service short names, or
RM_UNIQUE_PROCESS structures that describe running applications.
It might be a little overengineered for your particular needs...
But if that is what you want, go ahead and grab the vs-project.
the only way I know of is to use the Win32 exclusive lock API which isn't too speedy, but examples exist.
Most people, for a simple solution to this, simply to try/catch/sleep loops.
Here is some code that as far as I can best tell does the same thing as the accepted answer but with less code:
public static bool IsFileLocked(string file)
{
try
{
using (var stream = File.OpenRead(file))
return false;
}
catch (IOException)
{
return true;
}
}
However I think it is more robust to do it in the following manner:
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);
}
In my experience, you usually want to do this, then 'protect' your files to do something fancy and then use the 'protected' files. If you have just one file you want to use like this, you can use the trick that's explained in the answer by Jeremy Thompson. However, if you attempt to do this on lots of files (say, for example when you're writing an installer), you're in for quite a bit of hurt.
A very elegant way this can be solved is by using the fact that your file system will not allow you to change a folder name if one of the files there it's being used. Keep the folder in the same file system and it'll work like a charm.
Do note that you should be aware of the obvious ways this can be exploited. After all, the files won't be locked. Also, be aware that there are other reasons that can result in your Move operation to fail. Obviously proper error handling (MSDN) can help out here.
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);
}
For individual files I'd stick with the locking suggestion posted by Jeremy Thompson.
I once needed to upload PDFs to an online backup archive. But the backup would fail if the user had the file open in another program (such as PDF reader). In my haste, I attempted a few of the top answers in this thread but could not get them to work. What did work for me was trying to move the PDF file to its own directory. I found that this would fail if the file was open in another program, and if the move were successful there would be no restore-operation required as there would be if it were moved to a separate directory. I want to post my basic solution in case it may be useful for others' specific use cases.
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
}
You can use my library for accessing files from multiple apps.
You can install it from nuget: Install-Package Xabe.FileLock
If you want more information about it check
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 method will return true only if can lock file exclusive for this object.
But app which uploading file must do it in file lock too.
If object is inaccessible metod returns false.
Would something like this help?
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)
{
}
}
Try and move/copy the file to a temp dir. If you can, it has no lock and you can safely work in the temp dir without getting locks. Else just try to move it again in x seconds.
I use this workaround, but i have a timespan between when i check the file locking with IsFileLocked function and when i open the file. In this timespan some other thread can open the file, so i will get IOException.
So, i added extra code for this. In my case i want load 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!!!!!");
}
}
What do you think? Can i change some thing? Maybe i did not have to use IsFileBeingUsed function at all?
Thanks
I'm interested to see if this triggers any WTF reflexes. I have a process which creates and subsequently launches a PDF document from a console app. However, I was dealing with a frailty where if the user were to run the process multiple times, generating the same file without first closing the previously generated file, the app would throw an exception and die. This was a rather frequent occurrence because file names are based on sales quote numbers.
Rather than failing in such an ungraceful manner, I decided to rely on auto-incremented file versioning:
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);
}
}
Probably some more care can be given to the catch block to ensure I'm catching the correct IOException(s). I'll probably also clear out the app storage on startup since these files are intended to be temporary anyways.
I realize this goes beyond the scope of the OP's question of simply checking if the file is in use but this was indeed the problem I was looking to solve when I arrived here so perhaps it will be useful to someone else.
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
}
}
I'm working on a videogame where I save/load player savegames using c#'s binaryformatter. This works 99% of the time, but sometimes a user's savegame will get corrupted somehow, and then the game won't be able to read the file. If I could detect when the game encounters this problem, though, I could tell it to load a backup copy of the last good savegame, though, which would be helpful for everyone.
This is how I'm loading the data:
if (File.Exists(Application.persistentDataPath + "/" + saveLoad.saveFileName))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/" + saveLoad.saveFileName, FileMode.Open);
saveLoad.savedGames = (List<savedGame_latest>)bf.Deserialize(file);
file.Close();
success = true;
}
By the way, this is the error when the game loads a corrupted file:
EndOfStreamException: Failed to read past end of stream
Any ideas? What I want is basically a way for the system to detect "oops no, that's corrupted" and to then be shunted to try and load the last safe backup instead.
well you have to open the file to check whether it is opening or not.
what you can do is make a function which check whether the file can be opened or not -
To check for corrupted file
protected virtual bool IsFileCorrupted(FileInfo file)
{
FileStream stream = null;
try
{
stream = File.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
// File is corrupted
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not corrupted
return false;
}
Since BinaryFormatter stops reading the stream when it finishes you can simply add some hash or checksum value after the saved content without breaking the functionality.
Catching just EndOfStreamException detects only one possible corruption anyway.
Saving (hashAlg can be any HashAlgorithm implementation):
new BinaryFormatter().Serialize(stream, savegame); // regular data
var hash = hashAlg.ComputeHash(stream.ToArray());
stream.Write(hash, 0, hash.Length); // hash
And loading:
int hashLength = hashAlg.HashSize / 8; // get hash size in bytes
var bytes = stream.ToArray();
hash = hashAlg.ComputeHash(bytes, 0, (int)stream.Length - hashLength);
if (!hash.SequenceEqual(bytes.Skip(bytes.Length - hashLength)))
throw new ArgumentException("Savegame Corrupted"); // gotcha!
savegame = (SaveGame)new BinaryFormatter().Deserialize(stream);
return savegame;
Try also online.
I would like to know if there is a way to create a file and set the Last Write Time (and other timestamp information) without allowing another process to acquire a lock to the file between these two operations.
The reason I want to do this is to fix an issue where antivirus acquires a lock to the file just after it has been created and still has the lock by the time the file attributes are being attempted to be set. Specifically the code I am working with is SevenZipSharp (no longer maintained as far as I can see).
Code that reproduces this issue is:
var filePath = "test.txt";
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
var bytes = Encoding.ASCII.GetBytes("Hello fail.");
fileStream.Write(bytes, 0, bytes.Length);
var fileInfo = new FileInfo(filePath);
fileInfo.CreationTime = DateTime.Now;
}
This produces the following exception when executing the last statement:
System.IO.IOException
"The process cannot access the file 'c:\test.txt' because it is being used by another process."
I am considering implementing the setting of the time attributes with a retry mechanism, but wondered if there was a more elegant solution.
As #Damien_The_Unbeliever mentioned, you need to get the file handle. Try this.
class Program {
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetFileTime(SafeFileHandle hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);
static void Main(string[] args) {
var filePath = "test.txt";
long when = DateTime.Now.AddDays(10).ToFileTime();
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite)) {
if (!SetFileTime(fileStream.SafeFileHandle, ref when, ref when, ref when)) {
throw new Win32Exception();
}
var bytes = Encoding.ASCII.GetBytes("Hello fail.");
fileStream.Write(bytes, 0, bytes.Length);
}
}
}
The problem is you're trying to access a file that's being used by your using statement. That's why you're getting the error. You need to finish your using statement, then you can assign the creation time on the file.
If the file is getting locked by other software, your best bet is to create a while loop to wait for the file.
Try the following:
var filePath = "test.txt";
DateTime creationTime;
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
var bytes = Encoding.ASCII.GetBytes("Hello fail.");
fileStream.Write(bytes, 0, bytes.Length);
creationTime = DateTime.Now;
}
int numTries = 0;
while (true)
{
++numTries;
try
{
// Attempt to open the file exclusively.
using (var fileInfo = new FileInfo(filePath))
{
// If we got this far the file is ready
fileInfo.CreationTime = creationTime;
break;
}
}
catch (Exception ex)
{
if (numTries > 10)
{
// Get out of it
Console.WriteLine("This joker still has your file, I'm out.");
break;
}
// Wait for the lock to be released
System.Threading.Thread.Sleep(500);
}
}
No, there is nothing you can do to prevent another process from locking a file.
You could try writing a temporary version of the file and then execute Robocopy using Process.Start to copy the file while setting the attributes. The original copy of the file becomes irrelevant - you could clean it up later.
That will depend on whether the method Robocopy uses to copy the file and set attributes is atomic.
Someone has already written a wrapper to avoid the ugliness of calling a command-line app from your .NET code - It's called RoboSharp and has a nuget package.
I'm writing a program in C# that needs to repeatedly access 1 image file. Most of the time it works, but if my computer's running fast, it will try to access the file before it's been saved back to the filesystem and throw an error:
"File in use by another process"
I would like to find a way around this, but all my Googling has only yielded creating checks by using exception handling. This is against my religion, so I was wondering if anyone has a better way of doing it?
Updated NOTE on this solution: Checking with FileAccess.ReadWrite will fail for Read-Only files so the solution has been modified to check with FileAccess.Read.
ORIGINAL:
I've used this code for the past several years, and I haven't had any issues with it.
Understand your hesitation about using exceptions, but you can't avoid them all of the time:
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;
}
You can suffer from a thread race condition on this which there are documented examples of this being used as a security vulnerability. If you check that the file is available, but then try and use it you could throw at that point, which a malicious user could use to force and exploit in your code.
Your best bet is a try catch / finally which tries to get the file handle.
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.
}
Use this to check if a file is locked:
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;
}
}
For performance reasons I recommend you read the file content in the same operation. Here are some examples:
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;
}
Try it out yourself:
byte[] output1 = Helper.ReadFileBytes(#"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(#"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(#"c:\temp\test.txt");
I recently came across this issue and found this: https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors.
Here, Microsoft describes the following method for checking if an IOException was due to a locked file:
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
Console.WriteLine("There is a sharing violation.");
}
Just use the exception as intended. Accept that the file is in use and try again, repeatedly until your action is completed. This is also the most efficient because you do not waste any cycles checking the state before acting.
Use the function below, for example
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
Reusable method that times out after 2 seconds
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);
}
Perhaps you could use a FileSystemWatcher and watch for the Changed event.
I haven't used this myself, but it might be worth a shot. If the filesystemwatcher turns out to be a bit heavy for this case, I would go for the try/catch/sleep loop.
The accepted answers above suffer an issue where if file has been opened for writing with a FileShare.Read mode or if the file has a Read-Only attribute the code will not work. This modified solution works most reliably, with two things to keep in mind (as true for the accepted solution also):
It will not work for files that has been opened with a write share mode
This does not take into account threading issues so you will need to lock it down or handle threading issues separately.
Keeping the above in mind, this checks if the file is either locked for writing or locked to prevent reading:
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;
}
You can return a task which gives you a stream as soon as it becomes available. It's a simplified solution, but it is a good starting point. It's thread safe.
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();
}
}
You can use this stream as usual:
using (var stream = await FileStreamGetter.GetStreamAsync())
{
Console.WriteLine(stream.Length);
}
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");
Hope this helps!
Aside from working 3-liners and just for reference: If you want the full blown information - there is a little project on Microsoft Dev Center:
https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4
Now found at:
https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs
From the Introduction:
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.
It works by connecting to the "Restart Manager Session".
The Restart Manager uses the list of resources registered with the session to
determine which applications and services must be shut down and restarted.
Resources can be identified by filenames, service short names, or
RM_UNIQUE_PROCESS structures that describe running applications.
It might be a little overengineered for your particular needs...
But if that is what you want, go ahead and grab the vs-project.
the only way I know of is to use the Win32 exclusive lock API which isn't too speedy, but examples exist.
Most people, for a simple solution to this, simply to try/catch/sleep loops.
Here is some code that as far as I can best tell does the same thing as the accepted answer but with less code:
public static bool IsFileLocked(string file)
{
try
{
using (var stream = File.OpenRead(file))
return false;
}
catch (IOException)
{
return true;
}
}
However I think it is more robust to do it in the following manner:
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);
}
In my experience, you usually want to do this, then 'protect' your files to do something fancy and then use the 'protected' files. If you have just one file you want to use like this, you can use the trick that's explained in the answer by Jeremy Thompson. However, if you attempt to do this on lots of files (say, for example when you're writing an installer), you're in for quite a bit of hurt.
A very elegant way this can be solved is by using the fact that your file system will not allow you to change a folder name if one of the files there it's being used. Keep the folder in the same file system and it'll work like a charm.
Do note that you should be aware of the obvious ways this can be exploited. After all, the files won't be locked. Also, be aware that there are other reasons that can result in your Move operation to fail. Obviously proper error handling (MSDN) can help out here.
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);
}
For individual files I'd stick with the locking suggestion posted by Jeremy Thompson.
I once needed to upload PDFs to an online backup archive. But the backup would fail if the user had the file open in another program (such as PDF reader). In my haste, I attempted a few of the top answers in this thread but could not get them to work. What did work for me was trying to move the PDF file to its own directory. I found that this would fail if the file was open in another program, and if the move were successful there would be no restore-operation required as there would be if it were moved to a separate directory. I want to post my basic solution in case it may be useful for others' specific use cases.
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
}
You can use my library for accessing files from multiple apps.
You can install it from nuget: Install-Package Xabe.FileLock
If you want more information about it check
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 method will return true only if can lock file exclusive for this object.
But app which uploading file must do it in file lock too.
If object is inaccessible metod returns false.
Would something like this help?
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)
{
}
}
Try and move/copy the file to a temp dir. If you can, it has no lock and you can safely work in the temp dir without getting locks. Else just try to move it again in x seconds.
I use this workaround, but i have a timespan between when i check the file locking with IsFileLocked function and when i open the file. In this timespan some other thread can open the file, so i will get IOException.
So, i added extra code for this. In my case i want load 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!!!!!");
}
}
What do you think? Can i change some thing? Maybe i did not have to use IsFileBeingUsed function at all?
Thanks
I'm interested to see if this triggers any WTF reflexes. I have a process which creates and subsequently launches a PDF document from a console app. However, I was dealing with a frailty where if the user were to run the process multiple times, generating the same file without first closing the previously generated file, the app would throw an exception and die. This was a rather frequent occurrence because file names are based on sales quote numbers.
Rather than failing in such an ungraceful manner, I decided to rely on auto-incremented file versioning:
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);
}
}
Probably some more care can be given to the catch block to ensure I'm catching the correct IOException(s). I'll probably also clear out the app storage on startup since these files are intended to be temporary anyways.
I realize this goes beyond the scope of the OP's question of simply checking if the file is in use but this was indeed the problem I was looking to solve when I arrived here so perhaps it will be useful to someone else.
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
}
}
I have an application that is running on a stand-alone panel PC in a kiosk (C#/WPF). It performs some typical logging operations to a text file. The PC has some limited amount of disk space to store these logs as they grow.
What I need to do is be able to specify the maximum size that a log file is allowed to be. If, when attempting to write to the log, the max size is exceeded, new data will be written to the end of the log and the oldest data will be purged from the beginning.
Getting the file size is no problem, but are there any typical file manipulation techniques to keep a file under a certain size?
One technique to handle this is to have two log files which are half the maximum size each. You simply rotate between the two as you reach the max size of each file. Rotating to a file causes it to be overwritten with a new file.
A logging framework such as log4net has this functionality built in.
Try using Log4Net
http://www.codeproject.com/KB/aspnet/log4net.aspx
There's no easy way to strip the data from the beginning of file. So you have several options:
Keep the log in several smaller log files and delete the oldest "chunks" if the total size of all log files exceeds your limit. This is similar to what you want to do, but on different level
Rename the log file to "log.date" and start a new log. Similar to (1) but not an option if you have limited disk space.
IF you have enough RAM and your log size is relatively small to fit in memory, you can do the following: map the whole file into memory using Memory-mapped file, then perform move operation by taking the data from the middle of the file and moving them to the beginning. Then truncate the file. This is the only way to easily strip the data from the beginning of the log file without creating a copy of it.
Linux os: check out logrotate - http://www.cyberciti.biz/faq/how-do-i-rotate-log-files/
Windows os: try googling windows logrotate. for example: http://blog.arithm.com/2008/02/07/windows-log-file-rotation/
I wanted a simple solution as well, but I didn't want to add another dependency so I made a simple method. This has everything you need other than the part of compressing the old file to a zip, which you can find here: Create zip file in memory from bytes (text with arbitrary encoding)
static int iMaxLogLength = 2000; // Probably should be bigger, say 200,000
static int KeepLines = 5; // minimum of how much of the old log to leave
public static void ManageLogs(string strFileName)
{
try
{
FileInfo fi = new FileInfo(strFileName);
if (fi.Length > iMaxLogLength) // if the log file length is already too long
{
int TotalLines = 0;
var file = File.ReadAllLines(strFileName);
var LineArray = file.ToList();
var AmountToCull = (int)(LineArray.Count - KeepLines);
var trimmed = LineArray.Skip(AmountToCull).ToList();
File.WriteAllLines(strFileName, trimmed);
string archiveName = strFileName + "-" + DateTime.Now.ToString("MM-dd-yyyy") + ".zip";
File.WriteAllBytes(archiveName, Compression.Zip(string.Join("\n", file)));
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to write to logfile : " + ex.Message);
}
}
I have this as part of the initialization / reinitialization section of my application, so it gets run a few times a day.
ErrorLogging.ManageLogs("Application.log");
I wouldn't use this for a file meant to be over say 1 Meg and it's not terribly efficient, but it works good if you need to solve a pesky problem of when you need a log file that you can't conveniently maintain. Make sure the log file exists before you use this though... or you could add code for it as well as checking the location exists, etc.
// This is how to call it
private void buttonLog_Click(object sender, EventArgs e)
{
c_Log.writeToFile(textBoxMessages.Text, "../../log.log", 1);
}
public static class c_Log
{
static int iMaxLogLength = 15000; // Probably should be bigger, say 200,000
static int iTrimmedLogLength = -1000; // minimum of how much of the old log to leave
static public void writeToFile(string strNewLogMessage, string strFile, int iLogLevel)
{
try
{
FileInfo fi = new FileInfo(strFile);
Byte[] bytesSavedFromEndOfOldLog = null;
if (fi.Length > iMaxLogLength) // if the log file length is already too long
{
using (BinaryReader br = new BinaryReader(File.Open(strFile, FileMode.Open)))
{
// Seek to our required position of what you want saved.
br.BaseStream.Seek(iTrimmedLogLength, SeekOrigin.End);
// Read what you want to save and hang onto it.
bytesSavedFromEndOfOldLog = br.ReadBytes((-1 * iTrimmedLogLength));
}
}
byte[] newLine = System.Text.ASCIIEncoding.ASCII.GetBytes(Environment.NewLine);
FileStream fs = null;
// If the log file is less than the max length, just open it at the end to write there
if (fi.Length < iMaxLogLength)
fs = new FileStream(strFile, FileMode.Append, FileAccess.Write, FileShare.Read);
else // If the log file is more than the max length, just open it empty
fs = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read);
using (fs)
{
// If you are trimming the file length, write what you saved.
if (bytesSavedFromEndOfOldLog != null)
{
Byte[] lineBreak = Encoding.ASCII.GetBytes("### " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " *** *** *** Old Log Start Position *** *** *** *** ###");
fs.Write(newLine, 0, newLine.Length);
fs.Write(newLine, 0, newLine.Length);
fs.Write(lineBreak, 0, lineBreak.Length);
fs.Write(newLine, 0, newLine.Length);
fs.Write(bytesSavedFromEndOfOldLog, 0, bytesSavedFromEndOfOldLog.Length);
fs.Write(newLine, 0, newLine.Length);
}
Byte[] sendBytes = Encoding.ASCII.GetBytes(strNewLogMessage);
// Append your last log message.
fs.Write(sendBytes, 0, sendBytes.Length);
fs.Write(newLine, 0, newLine.Length);
}
}
catch (Exception ex)
{
; // Nothing to do...
//writeEvent("writeToFile() Failed to write to logfile : " + ex.Message + "...", 5);
}
}
}