I have a multi-threaded program (3-4 threads). All the threads depend on a couple of parameters which are specified in an XML file.
Since the parameters in the XML file may be changed at any time by a user therefore, the different threads need to be notified about it and need to get the updated copy of parameters.
To monitor the changes in the XML file, I am using a FileWatcher as per the MSDN documentation.
clas ReadXML
{
//parameters
private static string Param1 = "";
private static string Param2 = "";
public static void ReadXmlParameters()
{
XmlDocument xDoc = new XmlDocument();
try
{
xDoc.Load(_ParameterFileDirrectory + #"\" + _ParameterFileDirrectory);
//parameters
Param1 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/IpAddress")).InnerText;
Param2 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/Username")).InnerText;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void CreateXMLWatcher()
{
try
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = _ParameterFileDirrectory;
/* 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 .xml files.
watcher.Filter = _ParameterFileFilename; // "ParameterFile.xml";
// 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;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 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);
if (e.ChangeType.ToString() == "Changed")
{
ReadXmlParameters(); //Read the Parameters from XML again
MyThreadClass1._waitTillParametersChange.Set(); //Notifying the thread that the parameters might have chnaged
}
}
}
The above implementation is working fine for me. I have to start the FileWatcher from the Main() using the following lines:
public static void Main()
{
ReadXml.ReadXmlParameters();
ReadXml.CreateXMLWatcher();
// Start other threads now
}
and then I start my other threads.
QUESTION: Since with the above-mentioned implementation, I have got Static methods and variables in my program so, I am wondering if this is the proper (at least acceptable) implementation of a FileWatcher or should I try to get rid of these static things by implementing ReadXml as a singleton class (or providing the same object to all the thread classes).
Related
I posted a question here but deleted it after I found a rather tedious solution
I am trying to write an app that can monitor multiple folders. I looked at the following solutions:
FIleSystemWatcher multiple folders (Dynamically)
Create multiple instances of the same FileSystemWatcher
Monitor multiple folders using FileSystemWatcher
I tried all their solutions. But what would happen is that it only worked for local folders. Not network drives.
My last implementation was a combination of all three:
public static void StartMultipleWatchers(string path)
{
string[] paths = path.Split(',');
foreach (string folderPath in paths)
{
try
{
string folderPathtrim = folderPath.Trim();
WatchFile(folderPathtrim);
}
catch(Exception ex)
{
Logger.Error(ex);
}
}
}
private static void WatchFile(string monitoredDir)
{
FileSystemWatcher fsw = new FileSystemWatcher(monitoredDir, "*.gz");
fsw.Changed += new FileSystemEventHandler(OnChanged);
fsw.Created += new FileSystemEventHandler(OnCreated);
fsw.EnableRaisingEvents = true;
fsw.IncludeSubdirectories = true;
Logger.Info($"Started loop Monitor of Folder: {monitoredDir}");
}
private static void OnCreated(object sender, FileSystemEventArgs e)
{
FileInfo fileInfo = new FileInfo(e.FullPath);
string value = $"Created: {e.FullPath}";
Logger.Info(value);
}
Again this solution only worked for local folder. Or if I only made one watcher that watched one network folder.
Then I tried this very tedious solution:
public static void TestManualWatchers()
{
var fsw1 = new FileSystemWatcher(#"\\lap.org.com\tool_data_odp_ws\metrology\CIM\DATA_READY\");
var fsw2 = new FileSystemWatcher(#"C:\TestPath\");
fsw1.Changed += OnChanged;
fsw1.Created += OnCreated;
fsw1.EnableRaisingEvents = true;
fsw1.IncludeSubdirectories = true;
fsw2.Changed += OnChanged;
fsw2.Created += OnCreated;
fsw2.EnableRaisingEvents = true;
fsw2.IncludeSubdirectories = true;
Logger.Info($"Started watching manual double files");
}
Where I manually create each watcher and it's own properties. What is the difference between this tedious way and the dynamic above?
Is there a way to actually dynamically create individual watchers?
This question already has answers here:
Notification when a file changes?
(3 answers)
Closed 8 years ago.
I have requirement to process file as soon as someone put the file in ftp location
and i want to create c# code on windows server
Thanks in advance
You need to use FileSystemWatcher.
using System;
using System.IO;
using System.Security.Permissions;
public class Watcher
{
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 (directory)");
return;
}
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[1];
/* 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;
// 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);
}
}
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
http://www.codeproject.com/Articles/26528/C-Application-to-Watch-a-File-or-Directory-using-F
I just started with C#, and i tried to create create a FileWatcher, which should print the content of a File, if it is changed:
{
public static void watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "Path";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "Filter";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
public static void OnChanged(object source, FileSystemEventArgs e)
{
using (TextReader r = File.OpenText("Path")) {
while ((s = r.ReadLine()) != null) {
Console.WriteLine(s);
}
r.Close();
}
}
static void Main()
{
watch();
}
}
So far the FileWatcher is working fine, but if I try to print the content it works once and no matter how long I wait the programm will stop working on the second change.
As far as I understood the "using" statement should free the file. The close command does not change anything at all.
The file is a very small text file and should not be a problem.
Is there anyway to force the program to free the File?
The following code works fine:
using System;
using System.IO;
namespace FileReadTest
{
internal class Program
{
public static FileSystemWatcher watch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "d:\\";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "test.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
return watcher;
}
public static void OnChanged(object source, FileSystemEventArgs e)
{
string s;
using (StreamReader r = new StreamReader(File.Open("d:\\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
while ((s = r.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
}
static void Main()
{
var watcher = watch();
Console.ReadKey();
watcher.Dispose();
}
}
}
Note that I've changed file reading routine to avoid some reading problems when a file is opened by an other program. FileSystemWatcher will also not get out of scope and will not be disposed accidentally.
I think instead of watcher.EnableRaisingEvents = true, in your case want to use watcher.WaitForChanged(WatcherChangeTypes.All), to make your program wait for changes indefinitely.
BTW, the statement r.Close() is redundant because you are already implicitly call Dispose() via using, which in turn calls Close().
EDIT: To be more specific: WaitForChanged of course just waits for one change and then returns, so if you want to wait for more changes, you can use a loop. Note that no event handler is needed if you use it this way.
while(true)
{
watcher.WaitForChanged(WaitForChanged.All);
// Do stuff with the changed file here, no event handler needed
using(var sr = new StreamReader(filePath))
{
// ...
}
}
the first thing to do is debug and verify if your method OnChanged gets called only once or also at the following edits of the monitored file, then you already know if the issue is with the lifetime / scope of the watcher or somewhere else.
Is this a console application? does it close or stays open after you call the watch method in the main?
You need to dispose the FileSystemWatcher.
public static void OnChanged(object source, FileSystemEventArgs e)
{
using (TextReader r = File.OpenText("Path")) {
while ((s = r.ReadLine()) != null) {
Console.WriteLine(s);
}
r.Close();
}
File((FileSystemWatcher)sender).Dispose();
}
I wrote program and need my own file watcher (loop that checks if file can be opened). Something like this:
while (loadedFiles.Count > 0 || isNeedReRead)
{
Thread.Sleep(1000);
if (isNeedReRead)
ReadFromRegistry();
foreach (var file in loadedFiles)
{
if (!IsFileLocked(file.Value))
{
// logic
}
}
}
Source: Is there a way to check if a file is in use?
Here is my solution:
try
{
using (Stream stream = new FileStream(
path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
stream.Close();
return false;
}
}
catch (IOException)
{
return true;
}
It works fine with Word, Excel. But if the process does not lock the file, this method doesn't help. For example if an open bitmap file is changing, IsFileLocked returns false.
Any ideas?
You can setup monitoring the file by using the System.IO.FileSystemWatcher you should be able to use that the NotifyFilter property (set to LastAccessTime) to detect when a particular file was last accessed.
void SetupWatcher()
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"C:\";
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess;
// 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);
}
Another option assuming this is windows is to enumerate the list of open file handles for each process. The code posted here has a decent implementation so all you have to do is call
DetectOpenFiles.GetOpenFilesEnumerator(processID);
However, if a process opens a file reads the contents into memory then closes the file, you will be stuck with the monitoring option (listed above), since the process does not actually have the file open any longer once it is read into memory.
Newbee alert. Problem: I populate a combo box, user makes a selection. I then create and enable a FSW. All works well, until user revisits combo box to make an alternate selection. At that point, another FSW is instantiated resulting in IO Exceptions based on 'file in use' errors. I need to switch off the FSW (or destroy the instantiation) when the user makes a subsequent selection in the combo box. Entire program is driven from a Winform with the combo box.
How do either toggle the FSW on/off, or destroy the FSW instantiation and allow a new, similar one to be created when the user revisits the combo box and makes another selection?
Code that calls for instantiation of the FSW:
private void MinAndGo()
{
if (strLblPtr != null)
{
if(strLblPtr != "None")
{
if (!CheckMyPrinter(strLblPtr))
{
MessageBox.Show(ForegroundWindow.Instance, "Printer is not ready. Make sure it's turned on "
+ "and has paper loaded.", "Printer Not Ready");
}
}
this.WindowState = FormWindowState.Minimized;
this.Activate();
bCreateWatcher = true;
Watchit();
}
}
Code for WatchIt(). I was intending on using the bool bCreateWatcher to toggle the FSW on and off.
private static void Watchit()
{
List<string> list = new List<string>();
list.Add("C:\\SAMMS\\pcl");
list.Add("C:\\SAMMS\\lbl");
foreach (string my_path in list)
{
Watch(my_path);
}
}
private static void Watch(string watch_folder)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.InternalBufferSize = 8192; //defaults to 4KB, need 8KB buffer
watcher.Path = watch_folder;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(OnCreated);
// Begin watching.
try
{
if (bCreateWatcher)
{
watcher.EnableRaisingEvents = true;
}
else
{
watcher.EnableRaisingEvents = false;
}
}
catch(Exception ex)
{
MessageBox.Show(ForegroundWindow.Instance, "FSW not set correctly" + ex, "FSW Error");
}
}
The FileSystemWatcher implements IDisposable. Therefore you should call Dispose to destroy the instance.
You can find more information here:
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.dispose(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
Ok, so it looks like you need to store your watcher somewhere, perhaps a dictionary keyed on the path? You'll also need to have the class that this is all contained in implement IDisposable, so that you can properly call Dispose() on any watchers you currently have open with the class is disposed of. (You should then ensure that the containing class is also properly disposed.)
I would refactor Watch() to something like this (could probably be better):
private static IDictionary<string, FileSystemWatcher> _openWatchers
= new Dictionary<string, FileSystemWatcher>();
private static void Watch(string watch_folder)
{
if (!bCreateWatcher)
{
if (_openWatchers.ContainsKey(watch_folder))
{
_openWatchers[watch_folder].Dispose();
_openWatchers.Remove(watch_folder);
}
return;
}
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.InternalBufferSize = 8192; //defaults to 4KB, need 8KB buffer
watcher.Path = watch_folder;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(OnCreated);
// Begin watching.
try
{
watcher.EnableRaisingEvents = true;
_openWatchers[watch_folder] = watcher;
}
catch(Exception ex)
{
MessageBox.Show(ForegroundWindow.Instance, "FSW not set correctly" + ex, "FSW Error");
}
}
And your Dispose() method:
public void Dispose()
{
foreach (FileSystemWatcher fsw in _openWatchers.Values)
{
fsw.Dispose();
}
}