I can't figure out for the life of me where the two arguments come from in the following code. I've compiled it in Visual Studio, and it works, but when you use the lambda expressions to add the anonymous methods to the delegates for the FileSystemWatcher, how do the methods ever receive those two arguments? Where do they come from? Does the FileSystemWatcher return an array with the two arguments when the .Changed or .OnChanged events occur? If so, I haven't been able to find documentation that explains that. Here is the code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyDirectoryWatcher
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** The File Watcher App *****\n");
// Establish the path to the directory to watch.
FileSystemWatcher watcher = new FileSystemWatcher();
try
{
watcher.Path = #"C:\MyFolder";
}
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Press Enter to continue...");
Console.ReadLine();
return;
}
// Set up the things to be on the lookout for.
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Specify what is done when a file is changed, created, or deleted.
watcher.Changed += (s, e) =>
{
Console.WriteLine("File: {0} {1}!", e.FullPath, e.ChangeType);
};
watcher.Created += (s, e) =>
{
Console.WriteLine("File: {0} {1}!", e.FullPath, e.ChangeType);
};
watcher.Deleted += (s, e) =>
{
Console.WriteLine("File: {0} {1}!", e.FullPath, e.ChangeType);
};
watcher.Renamed += (s, e) =>
{
// Specify what is done when a file is renamed.
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
};
// Begin watching the directory.
watcher.EnableRaisingEvents = true;
// Wait for the user to quit the program.
Console.WriteLine(#"Press 'q' to quit app.");
while (Console.Read() != 'q') ;
}
}
}
Just to pick one of the events...
watcher.Changed += OnChanged;
private void OnChanged(object sender, FileSystemEventArgs e){
// Handle event
}
You're actually adding a delegate to the Changed event's invocation list every time you do +=. In this case, the delegate defines a signature that requires two parameters of type object and FileSystemEventArgs.
You can shorthand this using lambdas:
watcher.Changed += (sender, args) => {};
You need to look at the documentation for the event to work out the signature (or use an IDE like Visual Studio):
https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher.changed
When the File System Watcher needs to invoke the event (to inform consumers something has happened), it will call all of the delegates in the events invocation list, passing the sender and FileSystemEventArgs.
Let me know if I'm not understanding the question. So the watcher.Changed is using a delegate. So you are passing in a block of code to execute when they get a certain action on there point. You are just passing them in code to run.
Here is a little more simple example.
static void Main(string[] args)
{
DisplayString();
Console.ReadLine();
}
public static void DisplayString()
{
RunAction( (textToDisplay) =>
{
Console.Write(textToDisplay);
});
}
private static void RunAction(Action<string> action)
{
action("This Is A Test");
}
So in the Action<string>; it is saying my delegate is expecting a string. Which is passed in here
action("This Is A Test");
So In your example there code is
public delegate void FileSystemEventHandler(object sender, FileSystemEventArgs e);
Which means there code is passing you those two objects which you use through the (s, e)
So the watcher.Changed is declared
Blockquote
public event FileSystemEventHandler Changed;
Related
I'm trying to write a simple console application that waits for a change to a file then reads just the last line of that file. The file watcher works and the change event fires. But I'm struggling to work out how read from the file.
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Path = "E:\\myFilePath";
watcher.Filter = "";
watcher.EnableRaisingEvents = true;
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
Console.ReadKey();
}
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine(File.ReadLines(e.FullPath).Last());
}
when testing (editing the file in notepad) it will work once, then after a second edit I get the error...
System.IO.IOException: 'The process cannot access the file '' because
it is being used by another process.'
I still ran into problems using lines.GetEnumerator().Dispose();
You can add a pause before opening the file, just chose your poison for doing so.
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
for (int x = 0; x <= 500000; x++)
{
int t = x;
}
Console.WriteLine(File.ReadLines(e.FullPath).Last());
}
I tried using SreamReader too but still ran into the same problem.
// Does not work
using (StreamReader r = new StreamReader(e.FullPath))
{
while (r.EndOfStream == false)
{
m = r.ReadLine();
}
r.Close();
}
Console.WriteLine("{0}\n", m);
The problem is due to an already open handle to the file. File.ReadLines uses an iterator internally which is Disposable. Use this to ensure Disposal during the lifetime of your event handler.
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
var lines = File.ReadLines(e.FullPath);
Console.WriteLine(lines.Last());
lines.GetEnumerator().Dispose();
}
This should guarantee disposal before the end of event handler.
Edit: In the case that a different process is holding a write lock to the file, use Thread.Sleep(ms) with a safe time as inspecting and releasing file handles is not feasible in C#.
As I am learning the Rx (Reactive extensions), I want to know the different between given 2 peice of code:
Code 1
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher(#"C:\Logs", "*.*");
watcher.EnableRaisingEvents = true;
var source = Observable.FromEvent<FileSystemEventHandler, FileSystemEventArgs>(handler =>
{
FileSystemEventHandler fsHandler = (sender, e) =>
{
handler(e);
};
return fsHandler;
},
fsHandler => watcher.Created += fsHandler,
fsHandler => watcher.Created -= fsHandler
);
source.Subscribe(x => Console.WriteLine(x.Name + "is created"));
Console.Read();
}
Code 2
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher(#"C:\Logs", "*.*");
watcher.EnableRaisingEvents = true;
watcher.Created += watcher_Created;
Console.Read();
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.Name.ToString());
}
What is the different between above 2 peice of code as it produce the same result?
Even I gone through the core part and found that both the code line execute on saparate thread, then what is the difference between these and why I use Rx in such scenarios??
Thanks in advance!
In this specific example, there are two potential benefits to using Rx (emphasis on potential):
A convenient way to "unwire" the event handler: calling Dispose on the subscription (the thing returned by the Subscribe call) will have the same effect as watcher.Created -= handler
A way to compose events coming from this source with other IObservable (and IEnumerable, for that matter) sources. For example, if your use case is "I need to know when a file is created, then written to three times, etc, etc", you can create multiple IObservable "watchers" from the various events exposed on the FileSystemWatcher, then create a query that will fire only when the correct conditions occur
in pseudo-LINQ:
var createEvents = <get created event handler>;
var changeEvents = <get changed event handler>;
var createThenChangeThenChangeThenChange =
from create in createEvents
where create.Name == "some file I care about"
from firstChange in changeEvents
from secondChange in changeEvents
from thirdChange in changeEvents
select new { create, firstChange, secondChange, thirdChange};
I'm fooling around with the FileSystemWatcher in 4.0. I find this very useful but am getting caught in a loop. I'm trying to monitor whenever an ini is changed and change it back to the correct default (long story) however the change event copying over the new file is causing it to drop into a loop ... Any Ideas > ? I played around with the idea of deleting and recreating thefile to avoid triggering the changed event but this leads to another set of issues with the program that I'd rather avoid. Also I'd imagine I could overwrite the text but this also poses the same issue. Thanks in advance for the help
static void Main() { Watch (#"\\NoFault2010\Lexis\Data\Setup\", "tmconfig.ini", true); }
static void Watch (string path, string filter, bool includeSubDirs)
{
using (var watcher = new FileSystemWatcher (path, filter))
{
watcher.Changed += FileChanged;
watcher.EnableRaisingEvents = true;
Console.WriteLine("Do Not Close ... \n\nThis is a Temporary Configuration Manager for Time Matters ... \n\n\nI'm Listening ............");
Console.ReadLine();
}
}
static void FileChanged (object o, FileSystemEventArgs e)
{
string _right_stuff = #"\\NOFAULT2010\Lexis\Data\Templates\Programs\tmconfig.ini";
string _working = #"\\NOFAULT2010\Lexis\Data\Setup\tmconfig.ini";
System.Threading.Thread.Sleep(2000);
File.Copy(_right_stuff, _working, true);
Console.WriteLine("File {0} has been {1}", e.FullPath, e.ChangeType);
MAIL_IT("SQLMail#lcjlawfirm.com", "TM Master.INI has been altered", "Check the Master INI and Yell At Ecopy Guy " + e.ChangeType + e.FullPath);
}
How would I unsubscribe from the event to avoid entering into this loop.
To temporarily disable the event while you're fiddling with the file yourself:
static void FileChanged (object o, FileSystemEventArgs e)
{
watcher.Changed -= FileChanged;
... correct the file here...
watcher.Changed += FileChanged;
}
Alternatively, you can use a guard variable to detect reentrant calls:
static bool reentrant = false;
static void FileChanged (object o, FileSystemEventArgs e)
{
if (reentrant)
return;
reentrant = true;
... correct the file here...
reentrant = false;
}
Note that you will also want to do exception handling within the method or your file watcher may become permanently disabled if a problem occurs.
I've written an application that depends on filesystemwatcher - and also, sometimes the fsw handler makes a change to a file.
I approached it in two ways - the first was to take the view that my code would be very quick in changing the file - so I did
fsw.EnableRaisingEvents = false;
//make my change
fsw.EnableRaisingEvents = true;
However, if you feel that other files might get changed during that time, you could log the time that you make the change and store that data somewhere...
say, Dictionary mapFileNameTimeChanged ...here you could store the file name...so in your handler you could do something like....
fsw_Changed(object sender, FileSystemEventArgs e)
{
lock (m_mapFileNameChanged)
{
if (m_mapFileNameChanged.ContainsKey(e.FullPath))
{
FileInfo fileInfo = new FileInfo(e.FullPath);
if (fileInfo.LastAccessTime == m_mapFileNameChanged[e.FullPath]
{
return;//not been changed since you last did something with it....
}
}
else
{
m_mapFileNameChanged.Remove(e.FullPath);//discard this now..it has changed since you last looked at it...need to look at it again!
}
}
//do things in your event handler...
lock (m_mapFileNameChanged)
{
// copy or change the file here...
FileInfo fileInfo = new FileInfo(e.FullPath);
m_mapFileNameChanged[strFullPathToFile] = fileInfo.LastAccessTime;
}
}
You could add a boolean (again at the class level) that you could use to track whether the changes were caused by you, and if so, just immediately exit your FileChanged method, ie:
static bool inEdit;
static void FileChanged (object o, FileSystemEventArgs e)
{
if (inEdit)
return;
inEdit = true;
// Do processing
inEdit = false;
}
Unsubscribe is easy, so I wonder if that was the question:
watcher.Changed -= FileChanged
Also, I would create some object to be SynchronizationObject for watcher. There is a problem that by default watcher raises events in new thread, and thus if you unsubscribe after new thread is created, you might run into the problems.
Also of note that FileSystemWatcher may raises multiple events for something you consider as single event, and it might influence functioning of your program.
If you make the watcher a class variable instead of a local variable, then your FileChanged method should be able to access it. Then you should be able to do something like
static void FileChanged (object o, FileSystemEventArgs e)
{
watcher.EnableRaisingEvents = false;
// Edit the file here
watcher.EnableRaisingEvents = true;
}
Below is my code for a File watcher class I wrote:
class FileWatcher
{
#region Method that begins watching
public static void watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = ConfigurationManager.AppSettings["OpticusFileLoc"];
watcher.Filter = ConfigurationManager.AppSettings["OpticusFileName"];
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Changed += new FileSystemEventHandler(OnChanged);
Console.Write("\nWatcher started. Press any key to end.\n");
watcher.EnableRaisingEvents = true;
}
#endregion
#region Trigger function on change
public static void OnChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine("File has been changed.\n");
//watcher.EnableRaisingEvents = false ;
//Program.Main();
}
#endregion
}
How can I, from the OnChanged method, set the watcher.EnableRaisingEvents flag to false?
I guess I could do it by moving the declaration of the FileSystemWatcher object outside the method it is in, but I'm wondering if there's another way to do it.
I guess I could do it by moving the
declaration of the FileSystemWatcher
object outside the method it is in,
but I'm wondering if there's another
way to do it.
That's exactly how you should do it (have watcher just be a member field of your FileWatcher class).
That said, you also could do it like this:
public static void OnChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine("File has been changed.");
var watcher = source as FileSystemWatcher;
if (watcher != null)
{
watcher.EnableRaisingEvents = false;
}
else
{
// Hmm... some other object called this method.
// Do you really want that to be allowed?
}
}
Notice the comment I added in the code above. Generally it's a bad call to make methods such as this public, as this makes it possible for any arbitrary code to call OnChanged even when doing so makes no sense. (You want for this method to capture the event of a file being changed; if it's actually callable from anywhere, then how do you know if the file was in fact changed or if this is just some random call from elsewhere?)
source is probably the FileSystemWatcher in question, so try this:
((FileSystemWatcher)source).EnableChangingEvents = false;
The object source parameter should be your FileSystemWatcher object, so you could just use that to shut it down.
public static void OnChanged(object source, FileSystemEventArgs e)
{
Console.WriteLine("File has been changed.\n");
(source as FileSystemWatcher).EnableRaisingEvents = false;
}
Is this the kind of thing you want to do?
public static void OnChanged(object source, FileSystemEventArgs e)
{
FileSystemWatcher watcher = (FileSystemWatcher)source;
watcher.EnableRaisingEvents = false ;
}
Whats the best way to monitor multiple folders (not subdirectories) using FileSystemWatcher in C#?
I don't think FSW supports monitoring multiple folders, so just instantiate one per folder you want to monitor. You can point the event handlers at the same methods, though, which should end up working like I think you want.
The easiest way is to create multiple instances of the FileSystemWatcher object.
http://www.c-sharpcorner.com/UploadFile/mokhtarb2005/FSWatcherMB12052005063103AM/FSWatcherMB.aspx
You'll have to make sure the you handle events between the two folders correctly:
Although some common occurances, such
as copying or moving a file, do not
correspond directly to an event, these
occurances do cause events to be
raised. When you copy a file, the
system raises a Created event in the
directory to which the file was copied
but does not raise any events in the
original directory. When you move a
file, the server raises two events: a
Deleted event in the source directory,
followed by a Created event in the
target directory.
For example, you create two instances
of FileSystemWatcher.
FileSystemWatcher1 is set to watch
"C:\My Documents", and
FileSystemWatcher2 is set to watch
"C:\Your Documents". Now, if you copy
a file from "My Documents" into "Your
Documents", a Created event will be
raised by FileSystemWatcher2, but no
event is raised for
FileSystemWatcher1. Unlike copying,
moving a file or directory would raise
two events. From the previous example,
if you moved a file from "My
Documents" to "Your Documents", a
Created event would be raised by
FileSystemWatcher2 and a Deleted event
would be raised by FileSystemWatcher
Link to FileSystemEventArgs
Out of the box, FileSystemWatcher only supports monitoring a single parent directory. To monitor multiple sibling directories, you would need to create multiple instances of FileSystemWatcher.
You can try cheating this behavior, however, by taking advantage of FileSystemWatcher's ability to include subdirectories. You can create an NTFS junction point (aka symbolic link) as a subdirectory from the directory you are watching. Mark Russinovich of Sysinternals fame has a utility called Junction to simplify creation and management of symlinks.
Note that you can only create symlinks to directories on your local machine.
Although this is an old question I decided to answer, because I couldn't find a good answer anywhere.
So, the aim was to monitor multiple folders (not subdirectories) using FileSystemWatcher? Here's my suggestion:
using System;
using System.IO;
using System.Security.Permissions;
using System.Collections.Generic;
namespace MultiWatcher
// ConsoleApplication, which monitors TXT-files in multiple folders.
// Inspired by:
// http://msdn.microsoft.com/en-us/library/system.io.filesystemeventargs(v=vs.100).aspx
{
public class Watchers
{
public static void Main()
{
Run();
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Run()
{
string[] args = System.Environment.GetCommandLineArgs();
// If a directory is not specified, exit program.
if (args.Length < 2)
{
// Display the proper way to call the program.
Console.WriteLine("Usage: Watcher.exe PATH [...] [PATH]";
return;
}
List<string> list = new List<string>();
for (int i = 1; i < args.Length; i++)
{
list.Add(args[i]);
}
foreach (string my_path in list)
{
Watch(my_path);
}
// Wait for the user to quit the program.
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
private static void Watch(string watch_folder)
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = watch_folder;
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching.
watcher.EnableRaisingEvents = true;
}
// 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);
}
}
}
Can you simply use multiple instances of the FileSystemWatcher, one for each directory?
You would have to instantiate multiple instances of the FileSystemWatcher object. Though you can bind the Events to the same method and use the sender object to determine which FileSystemWatcher triggered the event.
var fsw1 = new FileSystemWatcher();
var fsw2 = new FileSystemWatcher();
FileSystemEventHandler fsw_changed = delegate(object sender, FileSystemEventArgs e)
{
Console.WriteLine("{0} - {1}", (sender as FileSystemWatcher).Path, e.ChangeType);
};
fsw1.Changed += fsw_changed;
fsw2.Changed += fsw_changed;
or you can pass the paths in-code, to mark a certain range of the domain watched like in this :
multiple monitor link
hope this helps.