This question already has answers here:
Wait until file is unlocked in .NET
(16 answers)
Closed 7 years ago.
private void DisplayLastTakenPhoto()
{
string mypath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),"RemotePhoto");
var directory = new DirectoryInfo(mypath);
var myFile = directory.EnumerateFiles()
.Where(f => f.Extension.Equals(".jpg", StringComparison.CurrentCultureIgnoreCase) || f.Extension.Equals("raw", StringComparison.CurrentCultureIgnoreCase))
.OrderByDescending(f => f.LastWriteTime)
.First();
LiveViewPicBox.Load(myFile.FullName);
}
protected virtual bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
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;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
The problem is with the line:
LiveViewPicBox.Load(myFile.FullName);
Sometimes it's working fine sometimes i'm getting exception on this line say the file is in use by another process.
So i want to use the IsFileLocked method or some other method to check untill the file is not locked.
But if i will call this method before the line
LiveViewPicBox.Load(myFile.FullName);
It will check if the file locked only once. I need somehow to use while or somet other way to check if the file is locked over and over again until it's unlocked.
And only when it's unlocked to make the line LiveViewPicBox.Load(myFile.FullName);
public static bool IsFileReady(String sFilename)
{
// If the file can be opened for exclusive access it means that the file
// is no longer locked by another process.
try
{
using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
{
if (inputStream.Length > 0)
{
return true;
}
else
{
return false;
}
}
}
catch (Exception)
{
return false;
}
}
Place this in a loop and wait for it to return true.
Related
I am using this code to monitor creation of files in certain folder:
_watcher = new RecoveringFileSystemWatcher(SourceFolder, "*.xml");
_watcher.Created += (_, e) =>
{
ProcessFile(e.Name);
};
RecoveringFileSystemWatcher is a fileSystemWatcher wrapper. It's constructor is:
public RecoveringFileSystemWatcher (string path, string filter)
{
_containedFSW = new FileSystemWatcher(path, filter);
}
The process works as expected but for some files, randomly, an exception is thrown telling that the file is used by another process.
This is the method that is launched upon file creation:
var nfo = new FileInfo(filePath);
if (nfo.Exists)
{
var archivoXml = nfo.Name;
string archivo = String.Empty;
try
{
string content = Task.Run(async () => await GetFileContent(filePath)).Result;
if (String.IsNullOrEmpty(content))
return false;
XmlDocument xml = new XmlDocument();
xml.LoadXml(content);
//The rest of the method
}
}
the method GetFileContent is this:
private async Task<string> GetFileContent(string filePath)
{
string content = String.Empty;
try
{
Console.Write("ONE - "); InfoLog.Save($"ONE {filePath}");
using (StreamReader sr = new StreamReader(filePath))
{
Console.Write("TWO - "); InfoLog.Save($"TWO {filePath}");
content = await sr.ReadToEndAsync().ConfigureAwait(false);
Console.Write($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} - "); InfoLog.Save($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
sr.Close();
Console.WriteLine($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")}"); InfoLog.Save($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
}
}
catch (Exception ex)
{
InfoLog.Save($"XML file could be read -> {filePath}. See error log.");
ErrorLog.Save(ex);
}
return content;
}
Look at the log information I am writing to debug the process.
I got one case with a file called 1112186.xml.... this is recorded in the log:
18/12/2018 19:12:10 ONE D:\GestorDocumental\Origen\1112186.xml
18/12/2018 19:12:10 XML file could not be read -> D:\GestorDocumental\Origen\1112186.xml. See error log.
As you see, the exception is thrown at the "using" instruction.
If I see the full log, I can see that file, 1112186.xml, is never used before, so the only chance is that FSW keeps the file opened. I don't know why, but it seems this is happening.
It is clear also that this process is locking the file, because when I exit the console application and then run again, the file can be processed.
Any help about this, please?
thanks
Jaime
I usually use this method to check if file is locked. I got it from one of the link in stackoverflow.
public static bool IsFileClosed(string filepath)
{
bool fileClosed = false;
int retries = 20;
const int delay = 400; // set a delay period = retries*delay milliseconds
if (!File.Exists(filepath))
return false;
do
{
try
{
// Attempts to open then close the file in RW mode, denying other users to place any locks.
FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
fs.Close();
fileClosed = true; // success
}
catch (IOException) { }
retries--;
if (!fileClosed)
Thread.Sleep(delay);
}
while (!fileClosed && retries > 0);
return fileClosed;
}
This is a new class called FileTimerWatcher (it will have logger injected):
public FileTimerWatcher(ILogger logger) : base(logger)
{
if (timer == null)
{
// Create a timer with a 1.5 second interval.
// monitor the files after 1.5 seconds.
timer = new Timer(delay);
// Hook up the event handler for the Elapsed event.
timer.Elapsed += new ElapsedEventHandler(ProcessFolder);
timer.AutoReset = true;
timer.Enabled = true;
}
}
private void ProcessFolder(object sender, ElapsedEventArgs e)
{
var LastChecked = DateTime.Now;
string[] files = System.IO.Directory.GetFiles(SourceDirectory, somefilter, System.IO.SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
ProcessFile(file); // process file here
}
}
Directory.Exists(imgFolder.Path);
alternate method in win8.1 store app,
im tried search online but i do get result for file exists only not to check folder exists
In Windows 8.1 you need to do something like following.
The following method will check if files exists or not:
public async Task<bool> isFilePresent(string fileName)
{
bool fileExists = true;
Stream fileStream = null;
StorageFile file = null;
try
{
file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
fileStream = await file.OpenStreamForReadAsync();
fileStream.Dispose();
}
catch (FileNotFoundException)
{
// If the file dosn't exits it throws an exception, make fileExists false in this case
fileExists = false;
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
return fileExists;
}
or:
public async Task<bool> isFilePresent(string fileName)
{
var item = await ApplicationData.Current.LocalFolder.TryGetItemAsync(fileName);
return item != null;
}
From Check If File Exists in Windows Phone 8 and Win RT
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - from time to time check if a file exists and read from it
I'm using file watcher to check if a file was craeted in a directory. If it was, then I want to open it and remove to another directory.
My approach is also to use FileShare.None to an ensure exclusive access. What I did is:
class Program
{
private static void Main(string[] args)
{
FileSystemWatcher fileWatcher = new FileSystemWatcher();
fileWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
fileWatcher.Created += fileWatcher_Created;
fileWatcher.EnableRaisingEvents = true;
Console.ReadLine();
}
private static void fileWatcher_Created(object sender, FileSystemEventArgs e)
{
WorkOnFile(e.FullPath);
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
}
}
}
The bottom line is, that it necessary to do WorkOnFile() completely and only one time for one file. If a file being copied, then I need to call WorkOnFile() again until it has completely been copied and none process is using it.
How do I reach it?
If you need that file moved as fast as possible, you could try using FileWatcher to monitor 'LastWrite' and after a small delay, trying to move the file.
I had to do something very similar, but there was no hurry, just important that the file was moved reliably. So I instead used a System.Timers.Timer to run once a minute:
var scanDirectoryIn = new DirectoryInfo(folderIn);
foreach (var fileInfo in scanDirectoryIn.GetFiles())
{
if (fileInfo.Extension != ".csv") continue;
if (DateTime.UtcNow.Subtract(fileInfo.LastWriteTimeUtc).TotalMinutes < 5) continue;
try
{
fileInfo.MoveTo(folderOut + "\\" + fileInfo.Name);
}
catch (Exception) {}
}
//must be done completely. How do I ensure it?
private static void WorkOnFile(string fileName)
{
while(true){
try{
using (FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
Thread.Sleep(40000); // some long operations
break; //exit while() infinite loop
}
}
catch(Exception e){
//file is locked because being written. wait a few seconds then retry
Thread.Sleep(10000);
}
}
}
public static void WriteLine(string text)
{
StreamWriter log;
if (!File.Exists(Filename))
{
log = new StreamWriter(Filename);
}
else
{
log = File.AppendText(Filename);
}
while this method is processed, other process also call this method. There will be error occur "file has been acess by other process". How to solve this problem by waiting the previous process finish.
I think the op wants to wait until the filehandle is free to use and then write to the file. In this case you should try to get the filehandle, catch the exception and if the exception is because the file is accessed by another process then wait a short time and try again.
public static void WriteLine(string text)
{
bool success = false;
while (!success)
{
try
{
using (var fs = new FileStream(Filename, FileMode.Append))
{
// todo: write to stream here
success = true;
}
}
catch (IOException)
{
int errno = Marshal.GetLastWin32Error();
if(errno != 32) // ERROR_SHARING_VIOLATION
{
// we only want to handle the
// "The process cannot access the file because it is being used by another process"
// exception and try again, all other exceptions should not be caught here
throw;
}
Thread.Sleep(100);
}
}
}
Both processes need to create a FileStream where they specify a FileShare mode of Write. You can then also drop the test whether the file exists, and just use the Append FileMode.
I'm writing a program that uses FileSystemWatcher to monitor changes to a given directory, and when it receives OnCreated or OnChanged event, it copies those created/changed files to a specified directories. At first I had problems with the fact that OnChanged/OnCreated events can be sent twice (not acceptable in case it needed to process 500MB file) but I made a way around this and with what I'm REALLY BLOCKED with is getting the following IOException:
The process cannot access the file 'C:\Where are Photos\bookmarks (11).html' because it is being used by another process.
Thus, preventing the program from copying all the files it should.
So as I mentioned, when user uses this program he/she specifes monitored directory, when user copies/creates/changes file in that directory, program should get OnCreated/OnChanged event and then copy that file to few other directories.
Above error happens in all cases, if user copies few files that needs to overwrite other ones in folder being monitored or when copying bulk of several files or even sometimes when copying one file in a monitored directory.
Whole program is quite big so I'm sending the most important parts.
OnCreated:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
OnChanged:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
fileType:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
Copy:
private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
I checked everywhere in my program and whenever I use some file I use it in using block so even writing event to log (class for what I omitted since there is probably too much code already in post) wont lock the file, that is it shouldn't since all operations are using using statement block.
I simply have no clue who's locking the file if not my program "copy" process from user through Windows or something else.
Right now I have two possible "solutions" (I can't say they are clean solutions since they are hacks and as such not desirable). Since probably the problem is with fileType method (what else could lock the file?) I tried changing it to this, to simulate "blocking-until-ready-to-open" operation:
fileType:
private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
This is working as much as I could tell (test), but... it's hack, not to mention other deficients.
The other "solution" I could try (I didn't test it yet) is using GC.Collect() somewhere at the end of fileType() method. Maybe even worse "solution" than previous one.
Can someone pleas tell me, what on earth is locking the file, preventing it from opening and how can I fix that? What am I missing to see?
Thanks in advance.
The problem is most likely that the file is still being copied while you already try to access it. This can happen especially on large files.
You can try to check whether the file can be opened with write permissions before you actually start your processing. For details how to do that check here.
If you can influence the process creating the file there might be a better solution. First copy the file with a temporary extension, and then, after the copying is completed, rename it so that the FileSystemWatcher event will be triggered.
You can try with Volume Shadow Copies.
See www.codeproject.com/KB/dotnet/makeshadowcopy.aspx for more details.
FileSystemWatcher events trigger when the file begins the copy, not at the end, so it's common to run into this kind of errors.
Your first approach will work, however, I would recommend spinning the I/O intensive code on another thread, and using an incremental Sleep() instead of the busy waiting you do.
However, if you have access to the software that actually creates the files, the extension changing is a slightly less complicated solution. Just beware, that a xls filter on the FileSystemwatcher will match a file called myfile1.xls.temp, as I found that out the hard way :)