I have a program that roughly does this:
open a file to read from it.
close the file
Start a filewatcher to watch for changes in the file.
As soon as a change is detected, the filewatcher's EnableRaisingEvents flag is set to false and the process repeats from Step 1.
The problem is, after going from step 4 to step 1, it cannot read the file saying that it is being used by another Process.
Error I receive:
Unhandled Exception: System.IO.IOException: The process cannot access the file 'c:\test.xml' because it is being used by another process.
Whats going wrong? does the reader from Step 1 of my program still have the file open, or is some entirely different process accessing the file, or is it that filewatcher is still watching the file after moving to Step 1 from 4, despite setting the flag to false?
If your code is similar to this:
[STAThread]
static void Main(string[] args)
{
string file = "temp.txt";
ReadFile(file);
FileSystemWatcher fswatcher = new FileSystemWatcher(".\\");
fswatcher.Changed += delegate(object sender, FileSystemEventArgs e)
{
ReadFile(e.FullPath);
};
while (true)
{
fswatcher.WaitForChanged(WatcherChangeTypes.Changed);
}
}
private static void ReadFile(string file)
{
Stream stream = File.OpenRead(file);
StreamReader streamReader = new StreamReader(stream);
string str = streamReader.ReadToEnd();
MessageBox.Show(str);
streamReader.Close();
stream.Close();
}
If you are editing the file via notepad, then, when you click the save button, it keeps the file open, while as if when you just close the program and click save it doesn't. I do no know if this is a bug or an undocumented feature of notepad, but this just might be your problem. One way to fix this is to do the following:
In your anonymous delegate, or wherever you execute the call to ReadFile() call Thread.Sleep(1000), to have the program wait before reading the file and your code should work fine.
You can use a tool like Process Explorer from http://www.sysinternals.com to see who has the open handle to the process
The file is most likely held open by whatever caused the change notification to fire in the first place.
Beside other answers it is possible that when FileWatcher reacts file it not yet closed by that app. In step 1 try not to fail immediately but try several attempts with small delay.
Note: even if "file.txt" is open in Notepad, this code still works, because it is opening for read.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
ReadFromFile(#"C:\file.txt");
Console.ReadLine();
}
static void ReadFromFile(string filename)
{
string line;
using (StreamReader sr = File.OpenText(filename))
{
line = sr.ReadLine();
while (line != null)
{
Console.WriteLine(str);
line = sr.ReadLine();
}
sr.Close();
}
}
}
Or just:
string text = System.IO.File.ReadAllText(#"C:\file.txt");
The problem is that the FileSystemWatcher tells you immediately when the file was created. It doesn't wait for the file to be released.
For instance, when you copy a large file which takes 3 seconds to copy, so you have to open the file after that.
http://www.codeproject.com/Questions/461666/FileSystemWatcher-issue-in-windows-application
Wait until file is unlocked in .NET
There are a number of things that could be going on.
First, make sure you properly dispose of the file writer (close isn't good enough) by utilizing the using clause around everything that implements IDisposable.
Second, it you are simply reading, make sure you have the correct flags set when opening the file.
To go any further it would help if you provided a code block which showed how you were accomplishing this; particularly around the reading of the file
You can use this MS utility openfiles to obtain list of opened files and understand who has opened the file.
openfiles /query
Also it allow to disconnect files opened by network users.
openfiles /disconnect /id XXXX
If you want use it for local PC you should set Maintain Objects List global flag:
openfiles /local on
Follow the link to get more details.
Related
I wanted to test if a particular file is already open before trying to launch it, so I came up with this:
public void LaunchErrorLog()
{
var logFile = ConfigurationManager.AppSettings["Log"];
if (IsLogOpen(logFile))
return; //figure out how to give focus to other app later
var psi = new ProcessStartInfo(logFile);
psi.WindowStyle = ProcessWindowStyle.Maximized;
Process.Start(psi);
}
private bool IsLogOpen(string p)
{
try
{
using (var s = new FileStream(p, FileMode.Open, FileAccess.Read)){}
}
catch (IOException)
{
return true;
}
return false;
}
I'm testing using a .log file (just a text file) that I've got open in Baretail. The method always returns false regardless of whether or not the file is open. I tried opening it in Notepad, and it still returns false.
Basically, the end objective is to give focus to the application that has the file open, or launch the application/file if it's not already open. But this is always false so it just goes on and launches a new instance of Baretail with the file open.
Also tried the top solution found here;
Is there a way to check if a file is in use?
Notepad is a bad test application because it does not hold a lock open on the file. It streams in the file and closes the lock. Use Word to do the test and you will see different results. A file is only locked if a handle is kept open by an application. Word will lock files. The same holds true for "Baretail".
In other words, if "Baretail" streams the file in and closes the lock then this test will not work. You could do something hacky such as sniff around Win32 objects...window handles and title bars to extract the information...but be warned this kind of UI hacking is tricky and I would not consider the information to be reliable. There's no stopping some other program from using similar text in their title bars per se.
I believe that that method is always returning false because you are opening for read. Even if the file is open for write elsewhere, you should be able to read that file (thus no exception is thrown).
Try using a different FileAccess opetion ie
FileAccess.ReadWrite
Or you can also try different combinations of FileMode and FileOption. (Sorry not in front of a dev machine at this point)
how to find whether specific .txt file is opened in notepad?
I have tried solutions mentioned here
Is there a way to check if a file is in use?
But they work fine for Word and pdf file but not working for txt file opened in Notepad.
here is code I have wrote.
public bool IsFileOpen(string strFileName)
{
bool retVal = false;
try
{
if (File.Exists(pstrFileName))
{
using (FileStream stream = File.OpenWrite(pstrFileName))
{
try
{
}
catch (IOException)
{
retVal = true;
}
finally
{
stream.Close();
stream.Dispose();
}
}
}
}
catch (IOException)
{ //file is opened at another location
retVal = true;
}
catch (UnauthorizedAccessException)
{ //Bypass this exception since this is due to the file is being set to read-only
}
return retVal;
}
am i missing somthing here.??
My requirement:
I have application which works similar to VSS. When user checks out specific file and opens ,and try to check in the same, while it has opened. Application is suppose to throw a warning message.For that i have used the above functionality.Its working fine for word and pdf.
To expand on my comment. A file is only locked if a handle is kept open by an application. Word for example will open the file, read in the stream and maintain the handle so that other applications cannot delete that file while the user is working on it.
Notepad, and other applications, just open the file, read in the entire stream and then close the file releasing the lock they have. This means that the file is no longer locked and can be edited by another application or even deleted and Notepad will not care as it has its own copy in memory.
You could try and hack around with getting instances of Notepad and checking if a file is open but this is ultimately not a great idea. If the file is not locked then you should be free to do what you want with it.
This is a hack solution I just came up with, but it should work for you. This makes use of System.Diagnostics.
Process[] processes = Process.GetProcessesByName("notepad");
for (int i = 0; i < processes.Length; i++)
{
Console.WriteLine(processes[i].MainWindowTitle);
if (processes[i].MainWindowTitle.Equals("myFile.txt - Notepad"))
{
Console.WriteLine("The file myFile is Open!");
}
}
Console.ReadLine();
Hopefully that should do the trick. My example looks to see if an instance of notepad is open with the window title "myFile.txt - Notepad". The window name is always "filename.extension - Notepad" so you can handle that however you might need to.
I suppose you could make a call to System.IO.File.GetLastAccessTime(filePath). You could then poll the file every so often and when the access time changes you know the file has been opened, you can then fire an event that the file has been opened. See Jeffs post here:
Detect File Read in C#
You could also do this using the following tactic: It seems that notepad does hold some kind of lock on the hosting folder (try to delete the folder and you'll see you can't).
you could use the following code Using C#, how does one figure out what process locked a file? to check list of processes that lock the folder.
one of the processes will be your notepad.
you could them compare by Title as another answers mentioned.
if you're issuing the open of the file - you could save the PID and comapre it with one of the processes that returned.
I'm writing a windows forms application i C#, where I'll need to open a document/file (.doc, .pdf, .xlsx, .tiff etc.) to the user in an associated program installed on the client pc.
The file should be deleted as soon the user closes the displaying program.
I've tried several options for creating and opening the file, but haven't found the golden egg yet.
public static void databaseFileRead(string file_name, byte[] file)
{
path = file_name;
int file_size_int = file.Length;
FileStream create = new FileStream(#path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, file_size_int, FileOptions.DeleteOnClose);
create.Write(file, 0, file_size_int);
FileStream open = File.Open(#path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
}
In the above method I'm getting an IOException stating that "The process cannot access the file 'xxx' because it is being used by another process" on last line (FileStream open = ...).
public static void databaseFileRead(string file_name, byte[] file)
{
path = file_name;
int file_size_int = file.Length;
FileStream create = File.OpenWrite(#path);
var attributes = File.GetAttributes(#path);
File.SetAttributes(#path, attributes | FileAttributes.ReadOnly|FileAttributes.Temporary);
create.Close();
Process p = Process.Start(#path);
p.WaitForExit();
File.Delete(#path);
}
And in this method I'm also getting an IOException stating that "The process cannot access the file 'xxx' because it is being used by another process" on last line (File.Delete(#path);) meaning that the file is still in use, which is correctly. It seems that p.WaitForExit(); is not waiting for all programs e.g. OpenOffice...
Is it possible to open/display a file created with FileOptions.DeleteOnClose in an external program?
If so, how?
I do like the idea, that Windows is deleting the file as soon that the file isn't used anymore.
It is important that the files disappear from the users hard drive automatically, the best option, would be to read and open the file from a stream or equal. But as far that I have read, this is not possible...
SOLVED:
However I'm not sure if it's a prober way to catch the exception and call the closeIfReady method again until the file is released...
public static void databaseFileRead(string file_name, byte[] file)
{
var path = file_name;
int file_size_int = file.Length;
if (File.Exists(path))
{
File.Delete(path);
}
FileStream create = File.OpenWrite(path);
create.Write(file, 0, file_size_int);
var attributes = File.GetAttributes(path);
File.SetAttributes(path, attributes | FileAttributes.Temporary);
create.Close();
Process p = Process.Start(path);
while (!p.HasExited)
{
Thread.Sleep(500);
}
closeIfReady(path);
}
static void closeIfReady(string path)
{
try
{ File.Delete(#path); }
catch
{
Thread.Sleep(1000);
closeIfReady(path);
}
}
OK, after the commenting, here's a working 2nd method:
void Method2(string file_name, byte[] file)
{
var path = file_name;
int file_size_int = file.Length;
if (File.Exists(path))
{
File.Delete(path);
}
FileStream create = File.OpenWrite(path);
var attributes = File.GetAttributes(path);
File.SetAttributes(path, attributes | FileAttributes.Temporary);
create.Close();
Process p = Process.Start(path);
while (!p.HasExited)
{
Thread.Sleep(100);
}
File.Delete(path);
}
You don't strictly need the if File.Exists/File.Delete at the start, I just needed it for debugging in mine - probably a good idea to leave it in there however, since if someone exits the application before the file gets removed it will try and create it again.
the problem line in this method however was:
File.SetAttributes(#path, attributes | FileAttributes.ReadOnly|FileAttributes.Temporary); Because the file was created as read-only, you can't remove it with File.Delete. In windows explorer it doesn't care about this if you have admin, which is why you can remove it from there, but C# does care about file attributes. I changed that line to File.SetAttributes(#path, attributes | FileAttributes.Temporary); and it now works.
This question is the reference I used for your p.WaitForExit problem. I would suggest starting this on a different thread in your application since otherwise your app will hang while it's checking the thread has exited (turns white/grey in windows and tells you it's not responding). There are things you can do like invalidating visuals if you're using wpf, or redrawing your GUI so you can still use it but those are hacky and can consume a lot of CPU.
A couple of things.
First, if this is running in the main UI thread of a winforms app, your application will become non responsive using either solution.
Second, both solutions depend on a graceful exit from several different applications. This is risky.
If the external process fails to properly clean up the file handle, your solution would cause infinite recursion. I also would be uncomfortable depending on Process.HasExited as I have seen many threads regarding the reliability of this property.
I would suggest using a temporary directory relative to your application, and cleaning up that directory on both application exit and start (just in case your own app fails to exit gracefully).
I got a problem with reading a text file, it's not the reading it's self that is a problem it is when I should read it.
I got a server program (made by an external company so I got no control over it) where you can put files in. The server see's a new file has arrived reads it and does things with it and then he puts a result back. So I'm waiting for the file to be ready, but I can't read it to early cause I don't know if it exists already. So I'm doing this:
while (IsFileLocked(file))
{
Thread.Sleep(25);
}
private bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
Now this really helps to check if the file is readable and I never get any IOExceptions anymore when I'm reading. Now I start the reading but sometimes I just read an empty file. And I'm pretty sure it shouldn't be empty. It's not a bug of the server program (I'll explain later).
Here is the code where the problem starts:
using (StreamReader streamReader = file.OpenText())
{
while (!streamReader.EndOfStream)
{
// here starts the reading
}
}
But when I use this code I don't get the problem:
using (StreamReader streamReader = file.OpenText())
{
Thread.sleep(1000);
while (!streamReader.EndOfStream)
{
// here starts the reading
}
}
So my guess is that the file is empty when I can open it but it has no text in it.
I changed the Thread.sleep to 100 miliseconds and then I get the problem again. When I changed it to 500 the problem was solved again.
Now my software needs to run on a lot of different computers so I don't know how fast they are, but I'm afraid if I set the timer to low I might get problems on other computers. But I don't want my users to be waiting for there info to long.
Is waiting the only option or is there another way?
Will there be a problem on other computers with different hardware configuration?
Edit: I tried FileSystemWatcher, and it is not the solution!
Waiting for a file to be created in C# - SO
The tool you need to add is a FileWatcher (see MSDN System.IO.FileWatcher). This will allow you to monitor activity and add event handlers to response to file events. You can respond to file created by adding a handler for file changed.
Simply you can do like this:
http://olioul.wordpress.com/2012/01/08/4-text-readwrite-in-text-document/
My application use "FileSystemWatcher()" to raise an event when a TXT file is created by an "X" application and then read its content.
the "X" application create a file (my application detect it successfully) but it take some time to fill the data on it, so the this txt file cannot be read at the creation time, so im
looking for something to wait until the txt file come available to reading. not a static delay but something related to that file.
any help ? thx
Create the file like this:
myfile.tmp
Then when it's finished, rename it to
myfile.txt
and have your filewatcher watch for the .txt extension
The only way I have found to do this is to put the attempt to read the file in a loop, and exit the loop when I don't get an exception. Hopefully someone else will come up with a better way...
bool FileRead = false;
while (!FileRead)
{
try
{
// code to read file, which you already know
FileRead = true;
}
catch(Exception)
{
// do nothing or optionally cause the code to sleep for a second or two
}
}
You could track the file's Changed event, and see if it's available for opening on change. If the file is still locked, just watch for the next change event.
You can open and read a locked file like this
using (var stream = new FileStream(#"c:\temp\file.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (var file = new StreamReader(stream)) {
while (!file.EndOfStream) {
var line = file.ReadLine();
Console.WriteLine(line);
}
}
}
However, make sure your file writer flushes otherwise you may not see any changes.
The application X should lock the file until it closes it. Is application X also a .NET application and can you modify it? In that case you can simply use the FileInfo class with the proper value for FileShare (in this case FileShare.Read).
If you have no control over application X, the situation becomes a little more complex. But then you can always attempt to open the file exclusively via the same FileInfo.Open method. Provide FileShare.None in that case. It will attempt to open the file exclusively and will fail if the file is still in use. You can perform this action inside a loop until the file is closed by application X and ready to be read.
We have a virtual printer for creating pdf documents, and I do something like this to access that document after it's sent to the printer:
using (FileSystemWatcher watcher = new FileSystemWatcher(folder))
{
if(!File.Exists(docname))
for (int i = 0; i < 3; i++)
watcher.WaitForChanged(WatcherChangeTypes.Created, i * 1000);
}
So I wait for a total of 6 seconds (some documents can take a while to print but most come very fast, hence the increasing wait time) before deciding that something has gone awry.
After this, I also read in a for loop, in just the same way that I wait for it to be created. I do this just in case the document has been created, but not released by the printer yet, which happens nearly every time.
You can use the same class to be notified when file changes.
The Changed event is raised when changes are made to the size, system attributes, last write time, last access time, or security permissions of a file or directory in the directory being monitored.
So I think you can use that event to check if file is readable and open it if it is.
If you have a DB at your disposal I would recommend using a DB table as a queue with the file names and then monitor that instead. nice and transactional.
You can check if file's size has changed. Although this will require you to poll it's value with some frequency.
Also, if you want to get the data faster, you can .Flush() while writing, and make sure to .Close() stream as soon as you will finish writing to it.