I've browsed quite a few threads on here and on other sites for solutions to this problem.
Here's my FileMonitor class:
class FileMonitor
{
public FileMonitor(String path)
{
try
{
var watcher = new FileSystemWatcher()
{
Path = path,
IncludeSubdirectories = true,
InternalBufferSize = 65536,
EnableRaisingEvents = true
};
watcher.Changed += new FileSystemEventHandler(OnFileChanged);
watcher.Created += new FileSystemEventHandler(OnFileCreated);
watcher.Deleted += new FileSystemEventHandler(OnFileDeleted);
watcher.Renamed += new RenamedEventHandler(OnFileRenamed);
watcher.Error += new ErrorEventHandler(OnWatcherError);
}
catch (Exception)
{
throw;
}
}
private void OnWatcherError(object sender, ErrorEventArgs e)
{
}
private void OnFileChanged(object sender, FileSystemEventArgs e)
{
try
{
((FileSystemWatcher)sender).EnableRaisingEvents = false;
LogFileSystemChanges(e);
}
finally
{
((FileSystemWatcher)sender).EnableRaisingEvents = true;
}
}
private void OnFileCreated(object sender, FileSystemEventArgs e)
{
try
{
((FileSystemWatcher)sender).EnableRaisingEvents = false;
LogFileSystemChanges(e);
}
finally
{
((FileSystemWatcher)sender).EnableRaisingEvents = true;
}
}
private void OnFileDeleted(object sender, FileSystemEventArgs e)
{
try
{
((FileSystemWatcher)sender).EnableRaisingEvents = false;
LogFileSystemChanges(e);
}
finally
{
((FileSystemWatcher)sender).EnableRaisingEvents = true;
}
}
private void OnFileRenamed(object sender, RenamedEventArgs e)
{
try
{
((FileSystemWatcher)sender).EnableRaisingEvents = false;
LogFileSystemRenaming(e);
}
finally
{
((FileSystemWatcher)sender).EnableRaisingEvents = true;
}
}
private void LogFileSystemChanges(FileSystemEventArgs e)
{
string log = string.Format("{0:G}: {1} | {2}", DateTime.Now, e.FullPath, e.ChangeType);
Console.WriteLine(log);
}
private void LogFileSystemRenaming(RenamedEventArgs e)
{
string log = string.Format("{0:G}: {1} | Old name: {2}", DateTime.Now, e.FullPath, e.OldName);
Console.WriteLine(log);
}
}
As you can tell, I have tried the "lock" of ((FileSystemWatcher)sender).EnableRaisingEvents = false;, but I can tell from my console output my events are triggering twice.
Any ideas on this? I'm really not sure where to go from here.
I've had the same problem and after much ado came to the conclusion that Notepad writes a file three times (!) in row. If an app really saves that much often (maybe to triangulate backups and such) there isn't much you can do.
I also noticed you have attached to all events, you should narrow it down to the least you need and filter out those you aren't interested in with the NotifyFilters.
I've tried to use the FileSystemWatcher class before (many years ago it turns out - 2008), and experienced major problems. It is a leaky abstraction at best. I reported my findings back then on CodeProject. Look for "Glytzhkof" in the comments list. As I recall I had problems with just about every aspect of the class, but it might have been improved now after so many years.
In summary my experience back in the day was that events disappeared altogether or piled up causing exceptions based on variables such as whether a disk's write cache was enabled, whether RAID, NAS or regular disks were accessed, and other random hardware issues that I do not recall exactly. Interestingly it seems it worked with UNC paths. No idea why. Mapped drives failed.
Have a look at the summary in the Code Project discussion. I am sure there are improvements since I tried it out, and a few new bugs perhaps :-). It seems disk storage hardware is hard to deal with as a high level abstraction - it leaks. Personally I ended up scanning for files manually using a service and regular disk functions. I wasted a lot of time before doing things manually.
UPDATE: See new info here: https://stackoverflow.com/a/23704476/129130
Related
i have written a service in C# which should move backup files (*.bak and *.trn) from a database server to a special backup server. This works quite well so far. The problem is that it tries to move single files twice. This fails of course. I have configured FileSystemWatcher as follows:
try
{
m_objWatcher = new FileSystemWatcher();
m_objWatcher.Filter = m_strFilter;
m_objWatcher.Path = m_strSourcepath.Substring(0, m_strSourcepath.Length - 1);
m_objWatcher.IncludeSubdirectories = m_bolIncludeSubdirectories;
m_objWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess; // | NotifyFilters.CreationTime;
m_objWatcher.Changed += new FileSystemEventHandler(objWatcher_OnCreated);
}
catch (Exception ex)
{
m_objLogger.d(TAG, m_strWatchername + "InitFileWatcher(): " + ex.ToString());
}
Is it possible that the Watcher produces an event twice for the same file? If I set the filter to CreationTime only, it does not react at all.
How do I have to set the Watcher to fire an event only once per file?
Thanks in advance for your help
The documentation states that common file system operations might raise more than one event. Check under the Events and Buffer Sizes heading.
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.
It also offers a few guidelines, including:
Keep your event handling code as short as possible.
To that end, you could use your FileSystemWatcher.Changed event to queue files for processing, then process them later. This is a quick example of what that might look like using an instance of System.Threading.Timer to process the queue.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
public class ServiceClass
{
public ServiceClass()
{
_processing = false;
_fileQueue = new ConcurrentQueue<string>();
_timer = new System.Threading.Timer(ProcessQueue);
// Schedule the time to run in 5 seconds, then again every 5 seconds.
_timer.Change(5000, 5000);
}
private void objWatcher_OnChanged(object sender, FileSystemEventArgs e)
{
// Just queue the file to be processed later. If the same file is added multiple
// times, we'll skip the duplicates when processing the files.
_fileQueue.Enqueue(e.FilePath);
}
private void ProcessQueue(object state)
{
if (_processing)
{
return;
}
_processing = true;
var failures = new HashSet<string>();
try
{
while (_fileQueue.TryDequeue(out string fileToProcess))
{
if (!File.Exists(fileToProcess))
{
// Probably a file that was added multiple times and it was
// already processed.
continue;
}
var file = new FileInfo(fileToProcess);
if (FileIsLocked(file))
{
// File is locked. Maybe you got the Changed event, but the file
// wasn't done being written.
failures.Add(fileToProcess);
continue;
}
try
{
fileInfo.MoveTo(/*Your destination*/);
}
catch (Exception)
{
// File failed to move. Add it to the failures so it can be tried
// again.
failutes.Add(fileToProcess);
}
}
}
finally
{
// Add any failures back to the queue to try again.
foreach (var failedFile in failures)
{
_fileQueue.Enqueue(failedFile);
}
_processing = false;
}
}
private bool IsFileLocked(FileInfo file)
{
try
{
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read,
FileShare.None))
{
stream.Close();
}
}
catch (IOException)
{
return true;
}
return false;
}
private System.Threading.Timer _timer;
private bool _processing;
private ConcurrentQueue<string> _fileQueue;
}
Credit where it's due, I took FileIsLocked from this answer.
Some other things you might need to consider:
What happens if your FileSystemWatcher misses an event? [The documentation] does state that it is possible.
Note that a FileSystemWatcher may miss an event when the buffer size is exceeded. To avoid missing events, follow these guidelines:
Increase the buffer size by setting the InternalBufferSize property.
Avoid watching files with long file names, because a long file name contributes to filling up the buffer. Consider renaming these files using shorter names.
Keep your event handling code as short as possible.
What happens if your service crashes, but the process writing backup files continues to write them? When you restart your service, will it pick those files up and move them?
I tried all sorts of ideas to stop this. The events are too close together... it can't be stopped in the FileChanged Event. Here is my working solution:
private System.Timers.Timer timer;
private FileSystemWatcher fwatcher;
static void Main(string[] args)
{
new Program();
}
private Program()
{
timer = new System.Timers.Timer(100);
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.AutoReset = false; // only once
fwatcher = new FileSystemWatcher();
fwatcher.Path = filePath;
fwatcher.Filter = fileName;
fwatcher.NotifyFilter = NotifyFilters.LastWrite;
fwatcher.Changed += new FileSystemEventHandler(FileChanged);
fwatcher.EnableRaisingEvents = true;
while (IsRunning)
{
Thread.Sleep(100);
}
Thread.Sleep(100);
}
private void FileChanged(object sender, FileSystemEventArgs e)
{
timer.Start();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("file has changed!");
}
The timer will only fire once each time the file is changed.
Yes there are many similar questions but none of them address my unique situation.
There is a separate c++ process writing the file using c++ printf and fprintf.
filename i am trying to watch is info_20160525.log
My fileSystemWatcher in winform C# application gets notification when the writer process writes to the file AND I physically access the file i-e F5 the folder or have it open in textpad and click the opened file or right click the file but I never get any event notification when I dont physically interact with the file.
Also, when I shutdown the writer application I do get the notification.
Here is my code.
public bool StartUp(string fullfilepath, int linenumber)
{
if (!File.Exists(fullfilepath))
return false;
if (!LogClass.CheckPathExists(m_applicationPath))
return false;
try
{
FileInfo info = new FileInfo(fullfilepath);
m_filename = fullfilepath;
m_fileWatcher = new FileSystemWatcher(info.DirectoryName, info.Name);
m_fileWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.LastAccess
| NotifyFilters.LastWrite | NotifyFilters.Size ;
m_fileWatcher.Changed += m_fileWatcher_Changed;
m_fileWatcher.Error += m_fileWatcher_Error;
m_fileWatcher.Created += m_fileWatcher_Created;
m_fileWatcher.Deleted += m_fileWatcher_Deleted;
m_fileWatcher.Disposed += m_fileWatcher_Disposed;
m_fileWatcher.Renamed += m_fileWatcher_Renamed;
m_linesRead = linenumber;
m_fileWatcher.EnableRaisingEvents = true;
}
catch(Exception e)
{
LogClass.LogError(e, "Trouble accessing the file" + fullfilepath, m_applicationPath);
}
return true;
}
These are the handlers. I have breakpoints in each one of them but I never get a trigger unless ofcourse I physically interact with the file.
void m_fileWatcher_Renamed(object sender, RenamedEventArgs e)
{
string S = "";
}
void m_fileWatcher_Disposed(object sender, EventArgs e)
{
string S = "";
}
void m_fileWatcher_Deleted(object sender, FileSystemEventArgs e)
{
string S = "";
}
void m_fileWatcher_Created(object sender, FileSystemEventArgs e)
{
string S = "";
}
void m_fileWatcher_Error(object sender, ErrorEventArgs e)
{
string S = "";
}
void m_fileWatcher_Changed(object sender, FileSystemEventArgs args)
{
if (args.ChangeType == WatcherChangeTypes.Changed)
{
while (ParseFile(args.FullPath))
{
}
}
}
I bet this thread has your answer --> FileSystemWatcher changed event (for “LastWrite”) is unreliable
The FileSystemWatcher uses an update to the LastWrite attribute of a file to fire events, however, the LastWrite is not updated in real time and should not be relied upon as trigger for an event.
If you have enough time and resources on your hands then you probably want to look into File System Filters and the simpler approach of a Filter Manager and Minifilter Driver. It is driver type development, however, it is a sure file way to accomplish your objective.
It is dug down a little deeper by system policy but gives you a wide array of events to latch onto. If I was doing this for anything like pci compliance or similar tasks then I would not use the FileSystemWatcher.
Make sure you set IncludeSubdirectories to true.
https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
please help me with the way to have a count for the number of times a file (.txt or .pdf) has been opened by user with the help of filesystemwatcher.
even after setting NotifyFilter-property to LastAccess, and catching the Changed-event. Like this:
FileSystemWatcher lWatcher = new FileSystemWatcher(#"C:\Documents and Settings\User\Desktop", "myfile.txt");
lWatcher.NotifyFilter = NotifyFilters.LastAccess;
lWatcher.Changed += new FileSystemEventHandler(HandlerWatcherChanged);
void HandlerWatcherChanged(object sender, FileSystemEventArgs e)
{
// file has been accessed
}
I have enabled Last Access time updates if necessary.
still I am not able to get that.
Perhaps your issue is related to this:
filesystemwatcherchanged-event-does-it-fire-when-a-file-is-accessed
EDIT:
Failing that try the following code:
FileSystemWatcher lWatcher = new FileSystemWatcher(#"C:\Documents and Settings\User\Desktop", "*.txt");
lWatcher.NotifyFilter = NotifyFilters.LastAccess;
lWatcher.EnableRaisingEvents = true;
lWatcher.Changed += new FileSystemEventHandler(HandlerWatcherChanged);
void HandlerWatcherChanged(object sender, FileSystemEventArgs e)
{
if (e.FullPath.Contains("mfile.txt"))
return; //do work here
}
My Excel AddIn is written in NetOffice, ExcelDNA, C#
It calls web service to get data. It takes a while to fetch a large amount of data.
During the process of data fetch, if network connection is lost, then Excel will hung, shows like "not responding". Now if I try to close Excel, it will ask you to close or debug. I simply close it.
Then when I restart Excel, there is an annoying message box comes up saying
"Excel experienced a serious problem with the 'commodity add-in' add-in. If you have seen this message multiple times, you should disable this add-in and check to see if an update is available. Do you want to disable this add-in?."
I wonder how to handle the situation when connection is lost appropriately? Thanks
Make the web service call asynchronously, if possible. Most WS will provide async versions and non-async versions of the calls that you can make.
If this is not possible, consider executing the web service data fetch within a separate thread.
In both scenarios, you should put some plumbing code in place to kill the job after a certain period, and probably some means to notify the user that not all is well.
"Excel experienced a serious problem with the 'XXX add-in' add-in. If
you have seen this message multiple times, you should disable this
add-in and check to see if an update is available. Do you want to
disable this add-in?."
You get this problem when an unhandled exception occurs. Excel will prompt you to disable the Add-In next start up. This can lead users to posts like this to fix it.
The pain is worse when you have to support clients using Citrix in non-admin environments. To get around the problem of Excel wanting to diable the add-In you have to add a Global Exception handler so the exception isn't referred back to Excel to avoid prompting users to disable the Add-In.
public YouAddInCtrl()
{
InitializeComponent();
// Add the event handler for handling UI thread exceptions to the event.
System.Windows.Forms.Application.ThreadException += ApplicationThreadException;
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += ApplicationUnhandledException;
}
private void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler(e.Exception);
}
private void ApplicationUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
addInManager.TopLevelExceptionHandler((Exception)e.ExceptionObject);
}
// Any exceptions returned to Excel will cause the Addin to be disabled
// So we must swallow them here.
internal void TopLevelExceptionHandler(Exception ex)
{
var e = new NotificationEventArgs(NotificationEventArgs.NotificationEnum.TopLevelException);
if (NotifyEventTopLevelException != null)
{
if (NotifyEventTopLevelException(ex,e))
{
System.Diagnostics.Process.Start("mailto:Support#XYZ.com%3e?subject=XYZ%202%20PROD%20Environment%20Problem&body=Hi,%0A%0AIssue:%0A%0ASteps%20to%20Reproduce:");
}
}
LogExceptions(ex);
}
I would also suggest that you run the WebService request on a different thread, eg:
BackgroundWorker1.WorkerReportsProgress = true;
BackgroundWorker1.WorkerSupportsCancellation = true;
BackgroundWorker1.DoWork += DoWorkExecuteQuery;
BackgroundWorker1.RunWorkerCompleted += RunWorkerCompletedExecuteQuery;
private bool QueryData()
{
var thinkProgBar = new ThinkingProgressBar();
thinkProgBar.ShowCancelLink(true);
thinkProgBar.SetThinkingBar(true);
BackgroundWorker1.RunWorkerAsync(thinkProgBar);
thinkProgBar.ShowDialog();
if (thinkProgBar.Tag != null && thinkProgBar.Tag.ToString() == "Cancelled")
{
CancelGetDataByFilters();
thinkProgBar.SetThinkingBar(false);
return false;
}
thinkProgBar.SetThinkingBar(false);
return true;
}
private void DoWorkExecuteQuery(object sender, DoWorkEventArgs e)
{
dtQueryData = null;
e.Result = e.Argument;
((ThinkingProgressBar)e.Result).SetThinkingBar(true);
dtQueryData = WEBSERVICE.GetData(); //CALL YOUR WEBSERVICE HERE
}
private void RunWorkerCompletedExecuteQuery(object sender, RunWorkerCompletedEventArgs e)
{
var dlg = e.Result as ThinkingProgressBar;
if (dlg != null) {
((ThinkingProgressBar)e.Result).SetThinkingBar(false);
dlg.Close();
}
}
Here is the ThinkingProgress bar:
public partial class ThinkingProgressBar : Form
{
private System.DateTime startTime = DateTime.Now;
public ThinkingProgressBar()
{
InitializeComponent();
}
private void lblClose_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
this.Tag = "Cancelled";
this.Hide();
}
public void ShowCancelLink(bool show)
{
lblClose.Visible = show;
}
public void SetThinkingBar(bool on)
{
if (on)
{
lblTime.Text = "0:00:00";
startTime = DateTime.Now;
timer1.Enabled = true;
timer1.Start();
}
else
{
timer1.Enabled = false;
timer1.Stop();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
var diff = new TimeSpan();
diff = DateTime.Now.Subtract(startTime);
lblTime.Text = diff.Hours + ":" + diff.Minutes.ToString("00") + ":" + diff.Seconds.ToString("00");
lblTime.Invalidate();
}
}
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;
}