C# FileSystemWatcher not triggering correctly in Service - c#

I'm working on a service where in my OnStartmethod I have the following lines of code to set up my FileSystemWatcher
Log.Info($"File location {_location}");
var watcher = new FileSystemWatcher(_location);
watcher.Changed += new FileSystemEventHandler(OnChanged);
Then in my OnChanged method I am wanting to start a timer like so:
private void OnChanged(object source, FileSystemEventArgs e)
{
Log.Info($"A file has been placed in {_location} starting timer");
OnTimer(null, null); //run immediately at startup
StartEventTimer();
}
The timer code works, so I know that isn't an issue, likewise in my log I know it is checking for the correct location. What is it that I'm missing?
All I'm wanting my code to do is to trigger my timer, the moment a file is placed in my target location yet I've not been able to do so. Am I correct in that I should be using FileSystemWatcherto do this, or should I use something else as this code is within a service?

You might well find that the Changed event is firing more than once on a new file, a common problem with some applications, which might create unwanted side effects later on. Have a look and try changing to Created instead.
If you're looking for a new file appearing in a folder, you should use:
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created += OnCreated;
Gist demonstrating it firing twice using Changed on LastWrite and a for predictable behaviour, a Gist demonstrating single fire on file create using Created and NotifyFilter.FileName
Just run it up in a Console App and copy a file into c:\temp.

There are a couple of things it could be based on what you said there.
The first thing of note is that the declaration for var watcher looks like it's not a class variable and will go out of scope when it exits OnStart(). You'll need to move the declaration outside of that.
The second item of interest is that it looks like EnableRaisingEvents isn't being set. A working example of the FileSystemWatcher is below.
public class SomeService
{
private FileSystemWatcher _watcher;
public void OnStart()
{
// set up the watcher
_watcher = new FileSystemWatcher(_location);
_watcher.Path = path;
_watcher.NotifyFilter = NotifyFilters.LastWrite;
_watcher.Filter = "*.*";
_watcher.Changed += new FileSystemEventHandler(OnChanged);
_watcher.EnableRaisingEvents = true;
}
}
EDIT
As Ben Hall mentioned, it is possible that multiple events can be raised for the same file when a file is moved into the folder. As per the MSDN documentation;
Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher

Related

How to add to a queue from FileSystemWatcher Onchanged Event and trigger different actions based on which file is changed

I am very new to coding (3 days in) and working on a project using FileSystemWatcher.
My current project is monitoring file changes to trigger an action.
I have it working correctly where it is monitoring a folder, with currently a single .txt file in it, and using OnChanged (looking at lastwrite) it triggers the action as expected. This is all fine and works perfectly, so I'm happy that I understand and can implement a basic FSW with an OnChanged event and trigger the appropriate action (go me, this is only after 3 days of learning C#).
However, I am unsure how to scale this up to multiple .txt files in the same folder, with each file triggering it's own specific action.
Example:
A.txt, B.txt, and C.txt live in the same folder, using FSW whenever the files are changed I want to trigger an action specific to that file - so when OnChanged A.txt would trigger action a , and B.txt would trigger action b.
I imagine I will need to use queue's, possibly with BlockingCollection(?), however I do not understand how I can get the FSW to add to a queue in a way that it identifies WHICH file has been changed and then how to link that to the specific action for that file.
Any ideas how to mix FSW with queueing to trigger a set action?
Any help would be greatly appreciated.
I have added my base code below (showing the FSW and OnChanged with an example action) - apologies if messy as I'm still learning.
void Awake()
{
/* Filewatcher to monitor changes to files - lastwrite*/
var watcher = new FileSystemWatcher(#"C:Path\to\folder");
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += OnChanged;
watcher.Filter = "*.txt";
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
private static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.Writeline("Hello World");
}
The FileSystemEventArgs which is passed in the OnChanged handler has the following properties you can use to determine specifics of the action.
ChangeType Gets the type of directory event that occurred.
FullPath Gets the fully qualified path of the affected file or directory.
Name Gets the name of the affected file or directory
With those you will know which file changed.
How you link the file names to actions can be done in any number of ways.
You could create a simple function that links file name to a method, however you would need to compile if you needed to change the file name or action to execute.

How to get notified about the deletion of a directory watched by FileSystemWatcher?

I would like to be informed if a monitored directory gets deleted (/renamed/moved).
Perhaps the following pseudo C# code helps to understand my problem:
bool called = false;
var fsw = new FileSystemWatcher(path);
fsw.Error += delegate(object s, ErrorEventArgs args) {
Assert.That(args.GetException() is IOException);
called = true;
};
fsw.Deleted += delegate(object source, FileSystemEventArgs e) {
Assert.That(e.ChangeType, Is.EqualTo(WatcherChangeTypes.Deleted));
called = true;
};
Directory.Delete(path);
Thread.Sleep(1000);
Assert.That(called, Is.True);
If I call the WaitForChanged method an IOException is returned but I is there another way to get notified about this problem?
The mono implementation on Linux works fine and simply returns the Deleted event. But the .Net implementation on Windows seems to differ in the behaviour.
You could create a second FileSystemWatcher, one level up, and set the Filter and NotifyFilter to monitor only the directory and the events that you are interested in.
As you noticed there is no perfect built-in way to do this. Like Philip mentioned you could use a multi-level approach. But the FileSystemWatcher lacks some stability and can cause problems in fast operating network environments.
So if you need a high performing solution - especially over network - I strongly recommend a polling strategy with a timing that fits your needs or a hybrid of this and the prior to assist the FileSystemWatcher with an availability-poll.

FileSystemWatcher used to watch for folder/file open

I have browsed around but cannot find any information on what I am seeking, if there is another post that already goes over this then I apologize.
I am seeking help with code that will monitor a specific folder for when the folder is opened by another person or when a file under said folder is opened. At this point I can see when a user opens and modifies any files but if they just open the file to view it, it does not throw an event even when I add LastAccessed. Any information or help would be appreciated.
Folder name is C:\Junk
Code in C# 4.0:
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Run()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"C:\";
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "junk";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
// Define the event handlers.
private static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}
it does not throw an event even when I add LastAccessed.
Because NotifyFilters.LastAccessed specifies that you wish to retreive that property, not the event to subscribe to. The available events are Changed, Created, or Deleted, none of which does what you want.
You should take a look at the ReadDirectoryChangesW Win32 function, documented here. It can be passed a FILE_NOTIFY_CHANGE_LAST_ACCESS flag, which seems to deliver what you want:
Any change to the last access time of files in the watched directory or subtree causes a change notification wait operation to return.
Edit: disregard this, the FileSystemWatcher does internally pass NotifyFilters.LastWrite as int 32, which is the same as FILE_NOTIFY_CHANGE_LAST_ACCESS, to ReadDirectoryChangesW. That function then still does not notify on file access, I've tried.
Perhaps this is caused by this:
Last Access Time has a loose granularity that only guarantees that the time is accurate to within one hour. In Windows Vista, we've disabled updates to Last Access Time to improve NTFS performance. If you are using an application that relies on this value, you can enable it using the following command:
fsutil behavior set disablelastaccess 0
You must restart the computer for this change to take effect.
If you execute that on the command prompt, perhaps then the LastAccess will be written and the event will fire. I'm not going to try in on my SSD and don't have a VM ready, but on Windows 7 disablelastaccess seems to be enabled out-of-the-box.
If it still doesn't work when you disable that behavior, wait for Raymond Chen's suggestion box (or himself) to come by, usually there's a quite logical explanation for why the documentation does not seem to correctly describe the behaviour you encounter. ;-)
You may as well just scan the directory in a loop and look at the LastAccessed property of the Files. What are you trying to do when a user opens a certain file?
To get On-Access file path there is one solution of minifilter driver. You have to implement minifilter driver to get the requirements implemented.
You should set
watcher.Path = #"C:\junk";
and delete watcher.Filter line if event should fire for all files
Using Filter property you can set wildcards for matching files, for example *.txt
what you really need is NtQuerySystemInformation enumeration and a timer, that way you can scan the directory and see if any of the files are open. the filesystemwatcher will not give you this info
public void OnChanged(object sender, FileSystemEventArgs e)
{
string FileName = System.IO.Path.GetFileName(e.FullPath);
if(IsAvailable(System.IO.Path.Combine(RecievedPath,FileName)))
{
ProcessMessage(FileName);
}
}
private void ProcessMessage(string fileName)
{
try
{
File.Copy(System.IO.Path.Combine(RecievedPath,fileName), System.IO.Path.Combine(SentPath,fileName));
MessageBox.Show("File Copied");
}
catch (Exception)
{ }
}
private static bool IsAvailable(String filePath)
{
try
{
using (FileStream inputStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
{
if (inputStream.Length > 0)
{
return true;
}
else
{
return false;
}
}
}
catch (Exception)
{
return false;
}
}
Digvijay Rathore gave already an answer, in my opinion the only good answer, even if a bit too short.
I want just to add a few words and a link where interested users could start.
The FileSystemWatcher is useful just to monitor what is happening inside the monitored folder, but it's not able to monitor and intercept what the user (or the OS) is doing.
For example, with a FSW (FileSystemWatcher) you can monitor when a file/folder is created, deleted, renamed or changed in some way, and those events are unleashed AFTER the action is completed, not before nor while.
A simple FSW is not able to know if the user is launching an executable from the monitored folder, in this case it will simply generate no events at all.
To catch when an executable is launched (and tons of other "events") before it is launched and make some action before the code of the executable is loaded in memory, you need to write something at lower (kernel) level, that is, you need to build a driver, in this specific case a (minifilter) File System driver.
This is a good starting point to start to understand the basic of Minifilter Windows Drivers:
https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/file-system-minifilter-drivers

FileSystemWatcher getting too many event

I'm trying to implement a file watcher that will raise an event if the file content was changed.
The problem that once the file I am watching was modified, I am getting 2 events.
(I want to get it only once)
_automationStatusWatcher = new FileSystemWatcher(fileInfo.Directory.FullName,
fileInfo.Name);
_automationStatusWatcher.NotifyFilter = NotifyFilters.LastWrite;
_automationStatusWatcher.Changed += OnAutomationStatusChanged;
_automationStatusWatcher.EnableRaisingEvents = true;
The file that i'm watching is not intended to be recreated/moved/deleted/whatever. its purpose is to be an xml database file that i need to monitor it once it changes. i want to get only 1 event when the file is modified and to ignore the other events.
How can I do that?
Manual:
Note
Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
I had to cope with this problem some time ago. I wanted to be notified when a file was created. The problem was that the event is triggered when the file is still empty or still being written to, not when the new file is closed.
The solution was to create the file using a temporary name and when finished renaming the file to its final name. I then watched for the rename-event.
You could name the file to myfile.tmp while writing to it and when finished rename it to myfile.xml and watch the rename-event.
You could attempt to unhook your event handler once you catch a change until you are ready to accept more changes:
_automationStatusWatcher.Changed -= OnAutomationStatusChanged;
But that is not necessarily thread safe and you could get multiple firings anyway.
Another option is to use a semaphore variable to see if you are handling the change:
private bool _inStatusChange = false;
private void OnAutomationStatusChanged(object sender, args...)
{
if (_inStatusChange)
{
return;
}
else
{
_inStatusChange = true;
//do work
_inStatusChange = false;
}
}
You should use appropriate locking to keep access to the semaphore variable thread safe.
i meet this problem too, now i found out this way :
new Thread(() => {
while (true) {
var r = watch.WaitForChanged(WatcherChangeTypes.All);
this.Invoke(new ThreadStart(() => {
listBox1.Items.Add(string.Format("{0} {1} {2}", DateTime.Now, r.Name, r.ChangeType));
}));
}
}) { IsBackground = true }.Start();
its very similar to nio in java
I had a similar problem for checking updates in logging configurations.
I read about the multiple events problem of FileSystemWatcher. So I decided to implement another solution.
I check for every access to my configuration file if it has changed by comparing modified date. Later I added a latence (2 seconds) to avoid too many accesses to filesystem. Maybe you can also use that way.
If you look at the documentation for FileSystemWatcher it says that it will fire multiple times.
Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
I would recommend doing some kind of queuing mechanism so that you don't have multiple actions on the same file.

FileSystemWatcher - event not firing the second time

I have an application that launches other applications, and then waits for them to create a specific data file (it watches one application at a time). Each time an application is launch it watches a specific directory for a specific file to be created. I am using the FileSystemWatcher to do this (set it to the directory, then filter for the correct file name). This works great the first time (always), but the second application launched never fires the event. The only way it seems to fire the event is if I place a break-point in the event handler, or if I have a Thread.Sleep command in the event handler. This seems very strange to me...is there some race condition that I'm not aware of? Here is the code. Notice I have a Thread.Sleep(500). With this line the code works every time. Without it will fail. I'm really not comfortable relying on a Sleep command. I'm not sure what condition will cause that not to work as well.
public static void watchFiles(string path)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.Created += new FileSystemEventHandler(watcher_Handler);
watcher.EnableRaisingEvents = true;
}
public static void watcher_Handler(object sender, FileSystemEventArgs e)
{
//Hack - the sleep allows the second and third application to be caught by this event
Thread.Sleep(500);
switch (e.ChangeType.ToString())
{
case "Changed":
break;
case "Deleted":
break;
case "Created":
if (e.Name == "log.dat")
{
parseDataFile();
moveHTMLtoLMS();
}
break;
default:
break;
}
}
Anyone know why I need to have that Sleep (or break-point) to get the code to work a second time?
According to the documentation of the System.IO.FileSystemWatcher class:
The Windows operating system notifies your component of file changes in a buffer created by the FileSystemWatcher. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer with the InternalBufferSize property is expensive, as it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small yet large enough to not miss any file change events. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties so you can filter out unwanted change notifications.
It might be that the event isn't being consumed fast enough and the internal buffer isn't large enough to handle all the notifications. By default, the watcher handles FileName, DirectoryName, LastWrite notifications yet you only consume creation events (both file and directory). Are your applications running in quick succession? I'd try putting a delay between the invocations of your applications (instead of the event handler), use more specific filters (just the FileName notification or watch only for log files using the Filter property), increase the internal buffer size or any combination of the above. I think that should fix your problem.
public static void watchFiles(string path)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.Created += new FileSystemEventHandler(watcher_Handler);
watcher.EnableRaisingEvents = true;
}
The watcher variable is eligible for garbage collection at the end of this method. Instead of being a local variable, make it a class-level member as such:
private static FileSystemWatcher watcher;
public static void watchFiles(string path)
{
if (watcher != null)
{
watcher.EnableRaisingEvents = false;
watcher.Created -= new FileSystemEventHandler(watcher_Handler);
}
watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.Created += new FileSystemEventHandler(watcher_Handler);
watcher.EnableRaisingEvents = true;
}
You are listenting to only one "Created" event. You need to listen to all other ones too - OnChanged, OnDeleted - http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx
EDIT: Most programs will not "Create" file when one already exists. You can use FileMon (now Process Monitor - http://technet.microsoft.com/en-us/sysinternals/bb896645 ) to see what operations each program perform with your file.
I'm facing the exact same problem here (running Windows XP). Your hack solves the problem. I would like to add some notes that might be relevant.
In my case the filename is always the same: C:\blah.txt is created, deleted, created and so forth. Also, I'm using a trick to hide my application:
Integrator.StartMonitor(); // Start the file monitor!
Form f = new Form();
f.ShowInTaskbar = false;
f.ShowIcon = false;
f.StartPosition = FormStartPosition.Manual;
f.Location = new Point(-32000, -32000);
f.Show();
f.Hide();
Application.Run();
My file watcher works in debug mode or when I add the sleep-hack of yours. It certainly looks like a bug in the FileSystemWatcher.

Categories

Resources