Read last line of text file after FileSystemWatcher detects change - c#

I am new to c# so please forgive my ignorance, I am running a fileSystemWatcher on a text file. And it is working fine, I can do some simple tasks after the file has changed. All but what I want to do.
I am trying to read the last line of the text file that has changed with this code
public void File_Changed( object source, FileSystemEventArgs e )
{
string MACH1 = File.ReadText(#"C:\MACHINE_1.txt").Last();
if (MACH1=="SETUP")
{
MACHINE1IND.BackColor = Color.Green;
}
else
{
MACHINE1IND.BackColor = Color.Red;
}
}
It works fine inside a button but not after file watcher.
Says it cannot find file?

One thing to be aware of is that the FSW can issue multiple change notifications during a save operation. You have no way of knowing when the save is complete. As a result, you need to always wrap your code in a try..catch block and support retry after a timeout to allow the file write to be completed. Typically, I will try to move the file to a temp location where I will do my processing. If the move fails, wait a couple seconds and try again.

As Jim Wooley explains in his answer, the file operation might still be in progress, when FSW fires a Created or Changed event. If FSW is used to communicate between two applications and you are in control of the "sending" application as well, you can solve the problem as follows:
Write the information to a temporary file. Close the file. Rename the temporary file and give it a definitive name.
In the other application (the receiver) watch for the Renamed event using the FileSystemWatcher. The renamed file is guaranteed be complete.

You'll have to check if the file exists before accessing it.
public void File_Changed(object source, FileSystemEventArgs e)
{
string filePath = #"C:\MACHINE_1.txt";
if(!File.Exists(filePath)) //Checks if file exists
return;
string MACH1 = File.ReadText(filePath).Last();
if (MACH1=="SETUP")
{
MACHINE1IND.BackColor = Color.Green;
}
else
{
MACHINE1IND.BackColor = Color.Red;
}
}

Related

How to continuously poll a file location based on message received

My app receives messages from another system to indicate that a file has been posted in a folder and it then checks the folder to copy that file to another location. This works perfectly, but the file gets posted in the folder 20 minutes after the app gets the message. I'd like some help/ ideas on how I can create a delay without stopping the polling for each file.
Eg:
10:00am: Message received that 20180405_file1.pdf created
10:02am: Message received that 20180405_file2.pdf created
10:20am: check folder for 20180405_file1.pdf
10:22am: check folder for 20180405_ file2.pdf
I tried using System.Thread.Timer, and Timespan, but I don’t want the app to wait until first file is read and then process second file. I want it to be continuously processing other messages and read the files
Any help would be great
MSDN:
FileSystemWatcher listens to the file system change notifications and
raises events when a directory, or file in a directory, changes.
Check msdn for more detail.
What you need:
FileSystemWatcher fileWatcher;
private void watch()
{
fileWatcher = new FileSystemWatcher();
fileWatcher.Path = path;
fileWatcher.Created += new FileSystemEventHandler(OnCreated);
fileWatcher.EnableRaisingEvents = true;
}
private void OnCreated(object source, FileSystemEventArgs e)
{
WaitForFile(e.FullPath);
//Copy files to another directory.
}
private void WaitForFile(string fullPath)
{
while (true)
{
try
{
using (StreamReader stream = new StreamReader(fullPath))
{
break;
}
}
catch
{
Thread.Sleep(1000);
}
}
}
OnCreated will hit once I file is created but wait till it is written. There is no need for any timer as FileSystemWatcher will monitor new file creation.
You can use this only there is a particular folder where all your pdf files are generating after you get the message. If it is a common folder where there are other files too, you need to manage a list of pdf files name same you got from the message notification and then copied them based on condition in OnCreated.

Process cannot access the file exception

As it was common to guess what kind of reason for this exception. But let me explain the exact scenario which am facing . Please find the overview of my code block.
Task.Factory.StartNew(()=> Method1());
private void Method1()
{
//A process which loads the file and uploads it to server. If the file was large, it will take some amount of time.
using (var fileStream = System.IO.File.OpenRead(filePath))
{
//Upload file
}
//Once uploads deletes from local.
File.Delete(path);
}
Before uploading the file delete method was called as I have used separate tasks . So I get the exception that process cannot access the file.
I should delete the file once upload was over. Need some suggestions on that.
TPL Continuewith
you can also make use of contnuewith over here because
you want to updload file
once update done you want to delete file
than you can do this
Task t =Task.Factory.StartNew(()=> Method1());//remove delete file form method one
t.ContinueWith((as)=> {File.Delete(path);} );
there might be syntax error in above code so please solve it in visual studio
Singling Construct
As you are updloading and delete file on different threds that I suggest you
make use of signaling construct
public class test
{
private static AutoResetEvent event_2 = new AutoResetEvent(false);
public void uploadfile()
{
///do file updating
//than give signale
event_2.set();
}
public void deletefile()
{
event_2.WaitOne();
//delete file
}
}
it doesn't seem thread issue , it look like file you want to delete is not present so its better you check file exists or not by file exits method
if (File.Exists(path))
{
File.Delete(path);
}

C# - Waiting for a copy operation to complete

I have a program that runs as a Windows Service which is processing files in a specific folder.
Since it's a service, it constantly monitors a folder for new files that have been added. Part of the program's job is to perform comparisons of files in the target folder and flag non-matching files.
What I would like to do is to detect a running copy operation and when it is completed, so that a file is not getting prematurely flagged if it's matching file has not been copied over to the target folder yet.
What I was thinking of doing was using the FileSystemWatcher to watch the target folder and see if a copy operation is occurring. If there is, I put my program's main thread to sleep until the copy operation has completed, then proceed to perform the operation on the folder like normal.
I just wanted to get some insight on this approach and see if it is valid. If anyone else has any other unique approaches to this problem, it would be greatly appreciated.
UPDATE:
I apologize for the confusion, when I say target directory, I mean the source folder containing all the files I want to process. A part of the function of my program is to copy the directory structure of the source directory to a destination directory and copy all valid files to that destination directory, preserving the directory structure of the original source directory, i.e. a user may copy folders containing files to the source directory. I want to prevent errors by ensuring that if a new set of folders containing more subfolders and files is copied to the source directory for processing, my program will not start operating on the target directory until the copy process has completed.
Yup, use a FileSystemWatcher but instead of watching for the created event, watch for the changed event. After every trigger, try to open the file. Something like this:
var watcher = new FileSystemWatcher(path, filter);
watcher.Changed += (sender, e) => {
FileStream file = null;
try {
Thread.Sleep(100); // hack for timing issues
file = File.Open(
e.FullPath,
FileMode.Open,
FileAccess.Read,
FileShare.Read
);
}
catch(IOException) {
// we couldn't open the file
// this is probably because the copy operation is not done
// just swallow the exception
return;
}
// now we have a handle to the file
};
This is about the best that you can do, unfortunately. There is no clean way to know that the file is ready for you to use.
What you are looking for is a typical producer/consumer scenario. What you need to do is outlined in 'Producer/consumer queue' section on this page. This will allow you to use multi threading (maybe span a backgroundworker) to copy files so you don't block the main service thread from listening to system events & you can perform more meaningful tasks there - like checking for new files & updating the queue. So on main thread do check for new files on background threads perform the actual coping task. From personal experience (have implemented this tasks) there is not too much performance gain from this approach unless you are running on multiple CPU machine but the process is very clean & smooth + the code is logically separated nicely.
In short, what you have to do is have an object like the following:
public class File
{
public string FullPath {get; internal set;}
public bool CopyInProgress {get; set;} // property to make sure
// .. other properties if desired
}
Then following the tutorial posted above issue a lock on the File object & the queue to update it & copy it. Using this approach you can use this type approaches instead of constantly monitoring for file copy completion.
The important point to realize here is that your service has only one instance of File object per actual physical file - just make sure you (1)lock your queue when adding & removing & (2) lock the actual File object when initializing an update.
EDIT: Above where I say "there is not too much performance gain from this approach unless" I refere to if you do this approach in a single thread compare to #Jason's suggesting this approach must be noticeably faster due to #Jason's solution performing very expensive IO operations which will fail on most cases. This I haven't tested but I'm quite sure as my approach does not require IO operations open(once only), stream(once only) & close file(once only). #Jason approach suggests multiple open,open,open,open operations which will all fail except the last one.
One approach is to attempt to open the file and see if you get an error. The file will be locked if it is being copied. This will open the file in shared mode so it will conflict with an already open write lock on the file:
using(System.IO.File.Open("file", FileMode.Open,FileAccess.Read, FileShare.Read)) {}
Another is to check the file size. It would change over time if the file is being copied to.
It is also possible to get a list of all applications that has opened a certain file, but I don't know the API for this.
I know this is an old question, but here's an answer I spun up after searching for an answer to just this problem. This had to be tweaked a lot to remove some of the proprietary-ness from what I was working on, so this may not compile directly, but it'll give you an idea. This is working great for me:
void BlockingFileCopySync(FileInfo original, FileInfo copyPath)
{
bool ready = false;
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Path = copyPath.Directory.FullName;
watcher.Filter = "*" + copyPath.Extension;
watcher.EnableRaisingEvents = true;
bool fileReady = false;
bool firsttime = true;
DateTime previousLastWriteTime = new DateTime();
// modify this as you think you need to...
int waitTimeMs = 100;
watcher.Changed += (sender, e) =>
{
// Get the time the file was modified
// Check it again in 100 ms
// When it has gone a while without modification, it's done.
while (!fileReady)
{
// We need to initialize for the "first time",
// ie. when the file was just created.
// (Really, this could probably be initialized off the
// time of the copy now that I'm thinking of it.)
if (firsttime)
{
previousLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName);
firsttime = false;
System.Threading.Thread.Sleep(waitTimeMs);
continue;
}
DateTime currentLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName);
bool fileModified = (currentLastWriteTime != previousLastWriteTime);
if (fileModified)
{
previousLastWriteTime = currentLastWriteTime;
System.Threading.Thread.Sleep(waitTimeMs);
continue;
}
else
{
fileReady = true;
break;
}
}
};
System.IO.File.Copy(original.FullName, copyPath.FullName, true);
// This guy here chills out until the filesystemwatcher
// tells him the file isn't being writen to anymore.
while (!fileReady)
{
System.Threading.Thread.Sleep(waitTimeMs);
}
}

File is being used by another process

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.

wait for a TXT file to be readable c#

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.

Categories