I'm using VB 2005 Express, obviously with .NET framework 2.0, and I've run into something a little odd..
I have myself a program which runs in the background, and uses a FileSystemWatcher. It logs changes to a certain directory, indicating that users have changed settings, etc..., etc..
Anyhow, one of the apps which changes settings does it in such a way that it deletes a file, then immediately creates a new file. So, to separate this type of event from a straight up deletion event, I put this code in the event handler (deletedlist is just an arraylist):
VB Code:
Private Sub FW_Deleted(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs)
deletedlist.Add(e.FullPath.ToUpper)
deletetime.Interval = 2000
deletetime.Enabled = True
End Sub
Private Sub deletetime_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
deletetime.Stop() 'This is a "run once" timer.
For i As Integer = 0 To deletedlist.Count - 1
Dim SW As New IO.StreamWriter(path + "global.log", True)
Dim datestr As String = Date.Now.ToShortTimeString + " on " + Date.Now.ToShortDateString
SW.WriteLine(deletedlist(i).ToString + " was deleted at " + datestr)
SW.Close()
Threading.Thread.Sleep(500) 'Avoid windows file conflicts.
Next
deletedlist.Clear() 'Remove all items from list, as they've been handled.
End Sub
You will notice neither sub has a handles clause at the end. Fair enough. Here they are in the forms load event:
So, you would think this would work eh? The tick event never fires. Now, Sometimes I think I know what I'm doing, so I tried just dragging and dropping instead of using AddHandler, just incase I've got that all wrong. Still no dice.
I then started a new project, drag and dropped a winforms timer, and a filesystemwatcher object onto the form, and basically did the same thing without any other code, and it never fires the tick sub. I remove the code from the filesystemwatcher's event handler, stick it in a generic button click sub, and it works fine. Logically, next, I put a breakpoint on the filesystemwatcher's event code - as soon as I delete a file, boom, it fires. Line by line I step through the enabling and setting of the interval.. yet the tick event (which I've also got a breakpoint at) never fires.
Finally, I replace my Winforms timer with a System.Timers.Timer, change the words tick to elapsed, and everything's fine.
Any clue why the winforms timer.Tick event will not fire if Enabled is turned on by a deleted event of a FileSystemWatcher?
This is a guess as Ive not used 2005 yet. When updating a control on a form from a different thread sometimes it failed to work in 2003 (treeview mainly). You had to reference the main form thread. Theres a me.something or other for it.
I spose you could just create a new instance of the timer there and then and attempt to fire it off. As I say Ive not used wintimer, so its just a stab till someone whos used it comes along .
Maybe I wasnt clear enough. It doesnt matter that its on the same form, you must use the same thread that created the object, to "change/use" it (for the treeview control at least). If you tried to add a new node from an outside event for example, it would error.
Ive just looked it up and its called Invoke. The event raised by the FW is not the forms thread, so call this:
Note that Timer.Tick events are raised in the UI thread, so using delegates is not required. That's assuming that the Timer was created in the UI thread of course.
conipto is adding the handle manually from the load event (which I assume is called by an outside thread!)
But he said that he tried it in the designer too and it still didn't work. Apart from that, he's using AddHandler in the form's Load event handler, which must be executed in the UI thread. Either way, there's nothing here that I can see that is happening in a different thread. If there was then you'd have an exception being thrown when debugging as by default VS 2005 won't let you make cross-thread calls to control members.
Ok, so I tried exactly what you had, and it worked. Perhaps in my test I only drag and dropped the timer
However, if I take your code, and create my filewatcher and it's handler in code, the deleted event still fires, but I never see my timer tick.. (Adding Me in the Addhandler declaration has no effect either)
Ooooo thats a nice feature. I know we had to Invoke a few times to change controls on events (IP) from outside. I thought the load would be the forms thread but ran out of ideas .
heres what conipto said:
Finally, I replace my Winforms timer with a System.Timers.Timer, change the words tick to elapsed, and everything's fine.
Go on, try the delegate thing out, prove me wrong .
Ooooo thats a nice feature. I know we had to Invoke a few times to change controls on events (IP) from outside. I thought the load would be the forms thread but ran out of ideas .
heres what conipto said:
Finally, I replace my Winforms timer with a System.Timers.Timer, change the words tick to elapsed, and everything's fine.
Yes, but my brain still wants to know why.. after research MSDN says the System timer is more accurate anyways (so I'll probably keep using it in the future!) but I'd still like to know why it doesn't work that way..
OK, I've made a breakthrough. Grimfort was indeed on the right track. When you create an FSW at run time the default is for its events to be raised on a thread other than the UI thread. What you need to do is add this line to have its events raised on the UI thread:
OK, I've made a breakthrough. Grimfort was indeed on the right track. When you create an FSW at run time the default is for its events to be raised on a thread other than the UI thread. What you need to do is add this line to have its events raised on the UI thread:
VB Code:
myFileSystemWatcher.SynchronizingObject = Me
Excellent. I wonder how many other objects this is the case for?
The WinForms Timer is optimised for WinForms apps, which means it is probably tuned to work on the UI thread. I would have thought that if the call to its Start method wasn't going to work you would have had an exception thrown, but I guess that must just be the case for controls. The System.Timers.Timer class has no such affinity for the UI thread so its members must not care from where they are called. I don't think it has anything to do with the SynchronizingObject property of the Timer, although perhaps if you do set it for the FSW then it won't work with a Timers.Timer.