FileSystemWatcher not firing events when file written using fprintf or printf - c#

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

Related

FileSystemWatcher - Reading last line of file after update?

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#.

FileSystemWatcher events raising twice despite taking measures against it

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

Filesystemwatcher for file open notification

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
}

Lost network connection causes Excel to crash

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();
}
}

FileSystemWatcher, unsubscribe from the event

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;
}

Categories