Prevent thread from exiting VB.NET - vb.net

I have a thread in my project that handles FileSystem events (FileSystemWatcher) and raises an event when a file is created.
The problem I am facing is that, as soon as I execute this thread, it ends. For some reason, after the code is executed to start the FileSystemWatcher, the thread imminently exists (believing it has already executed all the code), and therefore no events can be raised.
I proposed a solution using the following code:
Do While True
Application.DoEvents()
Threading.Thread.Sleep(1)
Loop
This works because it prevents the thread from exiting, so it may continue with the events, and the FileSystemWatcher properly raises events when files are created.
However, I do not believe this is a sustainable method for ensuring the events are raised, as it uses more resources and seems like taking a shortcut.
Is there a way to properly ensure the thread remains running, so the FileSystemWatcher can raise events properly?
Thank you for your assistance.

Why do you want to initialize the FileSystemWatcher on a thread?
I think you do not need a separate thread for this.
But you can handle the fired events asynchronously with Async/Await.
Private Shared Async Sub OnChanged(source As Object, e As FileSystemEventArgs)
Await Task.Delay(1000)
End Sub

Add FileSystemWatcher1 to your form. In the properties for FileSystemWatcher1 set EnableRaisingEvents to True, modify the filter to account for the file(s) you want to look for (i.e *.txt or . or whatever you are watching for), set the NotifyFilter to "FileName, LastWrite", and specify the path you want to watch.
In your code add...
Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim Watcher As FileSystemWatcher = FileSystemWatcher1
AddHandler Watcher.Created, AddressOf OnCreated
Watcher.EnableRaisingEvents = True 'already configured in the control, but why not
End Sub
Private Shared Sub OnCreated(source As Object, e As FileSystemEventArgs)
'Actions to perform when file is created
End Sub

Related

Detecting when a BackgroundWorker has its job done? Depending on circumstances, it needs to be rerun

My class watches a directory for incoming files. It does do so with a FileSystemWatcher object, only monitoring the FSW's Created events.
On a Created event, I start a potentially time-consuming process (file-deserialization is needed, sending an event to the client using my class, in which all sorts of things might happen). Thus, I start a BackgroundWorker object to do all this work, ultimately culminating in the received file's removal.
However, during all this work, new files may appear. In the Created event I check, if the BGW is still busy, and if so, I just store the fully qualified name in a queue for later consumption.
Public Sub New(Path As String)
FSM = New FileSystemWatcher
With FSW
.Path = Path
AddHandler .Created, AddressOf pFileArrived
End With
BGW = New BackgroundWorker
With BGW
.WorkerReportsProgress = False
.WorkerSupportsCancellation = False
AddHandler .DoWork, AddressOf BGW_DoWork
AddHandler .RunWorkerCompleted,
AddressOf BGW_RunWorkerCompleted
End With
End Sub
Private Sub pFileArrived(sender As Object, e As FileSystemEventArgs)
pNotifyClient(e.FullPath)
End Sub
Private Sub pNotifyClient(sFullPath As String)
If Not BGW.IsBusy Then
BGW.RunWorkerAsync(sFullPath)
Else
MyQueue.Enqueue(sFullPath)
End If
End Sub
Private Sub BGW_DoWork(ByVal sender As Object,
ByVal e As DoWorkEventArgs)
'...
End Sub
But how can I find out, when the BGW is done?
I know, that there is the RunWorkerCompleted event. However, this event is fired from a real BGW instance still existing, so I can not go on and simply call it again from within the event handler.
Private Sub BGW_RunWorkerCompleted(sender As Object,
e As RunWorkerCompletedEventArgs)
'This won't work.
If MyQueue.Count > 0 Then
BGW.RunWorkerAsync(MyQueue.Dequeue)
End If
End Sub
What is the proper way of doing such things? Initializing a timer does spring to mind, but it doesn't seem right. (How much time should I give it? Should I loop for the BGW thread's end?)
Or should I consider another approach than invoking a BGW?
If you want to nuke this problem from the orbit, put an AutoResetEvent(false) somewhere and your BGW's last task should be to evt.Set() and your main thread can do evt.WaitOne(0) to just query the status. If there's a possibility of running multiple BGWs at the same time, you need some data structure to keep track of which ARE is associated with which BGW.
A larger investment would be switch to pure producer-consumer design, which involves a queue (which you already have in a form) and consumer thread(s) to dequeue work items and process them.

Making sure async tasks complete before vb.net application terminates

I'm creating a vb.net desktop application. This application includes some asynchronous functions. When the user closes the application via the red X in the upper-right corner, there is some logic to possibly run one or more of these async functions. The problem is, the program terminates before they are complete. I figured using "Await" in my call would do that, but apparently not.
I found this thread that talks about using ManualResetEvent, but I'm having trouble understanding all of it, especially since the question is in the context of a console app, and the MSDN documentation the answer links to is about specifying threads, not simply using async tasks. As an attempt at using it anyway, I tried adding this to my main form:
Public resetEvent As ManualResetEvent = New ManualResetEvent(False)
And immediately after the call to one of these functions, I added this (quote includes the call):
Await activeCount.SerializeAsync(activeCount)
resetEvent.WaitOne()
And at the end of my async function itself, before returning the Task, added this:
frmMain.resetEvent.Set()
I don't think I'm using that right, though. The program still terminates before it's complete anyway.
Even before that, I figured the best place for such a thing would be in ApplicationEvents MyApplication_Shutdown, but I'm not sure how to know if such a function is still running at that point.
So what is the best way to make sure all my async functions complete before the application terminates in this situation?
Thank you!
UPDATE AFTER ACCEPTED ANSWER:
Though F0r3v3r-A-N00b's answer worked, I realized I need to use a dialog in certain cases. I couldn't call that within the background worker because the dialog is on the GUI thread, not the background thread. I tried moving things around so I'd call the dialog first, then make the background worker and all that, but for whatever reason I couldn't get it to work.
Long story short, I got around it by simply making a synchronous version of my functions, and so I could say 'if the user terminated the program and I need to call any of these functions before closing, call the synchronous versions instead'. That works. Thanks!
Try this. Create a new project. Add 1 label and backgroundworker to your form. Paste this in your form's code area:
Public Class Form1
Dim taskCompleted As Boolean = False
Dim taskIsrunning As Boolean = False
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Threading.Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
taskCompleted = True
taskIsRunning = False
Label1.Text = "Background task completed."
Me.Close()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If taskIsRunning Then
e.Cancel = True
Exit Sub
End If
If Not taskCompleted Then
taskIsRunning = True
Label1.Text = "Starting background task."
BackgroundWorker1.RunWorkerAsync()
Label1.Text = "Background task is running."
e.Cancel = True
End If
End Sub
End Class

WinForms.IllegalCrossThreadCall with filewatcher

I'm new to Visual Basic and overall kind of new to coding in general.
Currently I work on a program which uses a filewatcher. But If I try this:
Public Class Form1
Private WithEvents fsw As IO.FileSystemWatcher
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
fsw = New IO.FileSystemWatcher("PATH")
fsw.EnableRaisingEvents = True
' fsw.Filter = "*.settings"
End Sub
Private Sub GetSettingsFromFile()
Some Code
More Code
CheckBox1.Checked = True
End Sub
Private Sub fsw_Changed(sender As Object, e As FileSystemEventArgs) Handles fsw.Changed
fsw.EnableRaisingEvents = False 'this is set because the file is changed many times in rapid succesion so I need to stop the Filewatcher from going of 200x (anyone has a better idea to do this?)
Threading.Thread.Sleep(100)
GetSettingsFromFile()
fsw.EnableRaisingEvents = True 'enabling it again
End Sub
End Class
But when I do this (trying to change anyhting in the form) I get this error:
System.InvalidOperationException (WinForms.IllegalCrossThreadCall)
It wont stop the program from working, but I want to understand what is wrong here and why the debugger is throwing this at me
regards
The event is being raised on a secondary thread. Any changes to the UI must be made on the UI thread. You need to marshal a method call to the UI thread and update the UI there. Lots of information around on how to do that. Here's an example:
Private Sub UpdateCheckBox1(checked As Boolean)
If CheckBox1.InvokeRequired Then
'We are on a secondary thread so marshal a method call to the UI thread.
CheckBox1.Invoke(New Action(Of Boolean)(AddressOf UpdateCheckBox1), checked)
Else
'We are on the UI thread so update the control.
CheckBox1.Checked = checked
End If
End Sub
Now you simply call that method wherever you are and whatever thread you're on. If you're already on the UI thread then the control will just be updated. If you're on a secondary thread then the method will invoke itself a second time, this time on the UI thread, and the control will be updated in that second invocation.

Problem monitoring directory for file activity in VB.net 2010

I'm trying to write a simple program to monitor a folder for new files in VB.NET 2010, and am having some trouble.
Here's a simplified version of what my program looks like:
Imports System.IO
Public Class Main
Public fileWatcher As FileSystemWatcher
Sub btnGo_Click(sender As System.Object, e As System.EventArgs) Handles btnGo.Click
'//# initialize my FileSystemWatcher to monitor a particular directory for new files
fileWatcher = New FileSystemWatcher()
fileWatcher.Path = thisIsAValidPath.ToString()
fileWatcher.NotifyFilter = NotifyFilters.FileName
AddHandler fileWatcher.Created, AddressOf fileCreated
fileWatcher.EnableRaisingEvents = True
End Sub
Private Sub fileCreated(sender As Object, e As FileSystemEventArgs)
'//# program does not exit when I comment the line below out
txtLatestAddedFilePath.Text = e.FullPath
'//# e.FullPath is valid when I set a breakpoint here, but when I step into the next line, the program abruptly halts with no error code that I can see
End Sub
End Class
As you can see, I have a button which will initialize a FileSystemWatcher when clicked. The initialization works, and when I place a new file in the monitored directory, the program reaches the fileCreated sub. I can even see that e.FullPath is set correctly. However, it exits abruptly right after that with no error code (none that I can see, anyways). If I comment everything in the fileCreated sub out, the program continues running as expected.
Any ideas as to why it's dying on me? Any help would be greatly appreciated. I'm fairly new to VS/VB.NET, so maybe I'm just making a silly mistake. Thanks!
Could be a cross-thread operation exception.
Try this:
Private Sub fileCreated(sender As Object, e As FileSystemEventArgs)
me.Invoke(New MethodInvoker(Function() txtLatestAddedFilePath.Text = e.FullPath))
End Sub
or (even better in your context), during fileWatcher initialization:
fileWatcher = New FileSystemWatcher()
fileWatcher.SynchronizingObject = me
[...]
Explanation:
http://www.blackwasp.co.uk/FileSystemWatcher.aspx (see Preventing Cross-Thread Operations)
Excerpt:
By default, when the FileSystemWatcher
object raises notification events, the
delegate calls are made on a thread
from the system thread pool. This will
generally not be the same thread as
that being used to control the form.
As the demonstration application will
require that the file changes be
logged within a visual element of the
form, using the allocated thread to
modify the list box contents would
result in a cross-threading operation
and an IllegalOperationException being
thrown.

How to handle this Multithread situation and don't lock?

I have this situation: a Form with a System.Timer in it (with AutoReset = False). The form has its main thread and the timer its own thread too (nothing new here).
When the user press a button I need to stop the timer, wait until the timer thread has stopped its execution and do something more.
On the other side, the timer updates an item at the form so BeginInvoke is used. The code looks like this:
Button Code:
Private Sub ButtonStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonStop.Click
SyncLock (m_stopLock)
m_stopProcessTimer = True
Threading.Monitor.Wait(m_stopLock)
End SyncLock
''#Do more things here after the timer has end its execution and is stopped
End Sub
Timer code:
Private Sub m_processTimer_Elapsed(ByVal sender As Object, ByVal e As System.EventArgs) Handles m_processTimer.Elapsed
Dim auxDelegate As EventHandler
SyncLock (m_stopLock)
If Not m_stopProcessTimer Then
If Me.InvokeRequired Then
auxDelegate = New EventHandler(AddressOf m_processTimer_Elapsed)
Me.BeginInvoke(auxDelegate, New Object() {sender, e})
Else
DoFormStuf()
m_processTimer.Start()
End If
Else
Threading.Monitor.Pulse(m_stopLock)
End If
End SyncLock
End Sub
The point is that I wait the main thread to let the timer thread to end its work.
The problem is that this code deadlocks when the user clicks the button when the BeginInvoke is going to be called. How a simple thing like this one can be done? Looks like I cannot find a good solution to this problem :(
Don't use locks at all, just make sure to do everything on the UI thread, and you can guarantee that nothing will be corrupted. Remember that dispatcher items run on the UI thread, so you know that if you're doing everything either in a dispatcher item or an event handler, only one thing is executing at a time.
1) Perhaps a little more code would be helpful. Do you create a new thread and put the timer ON that thread?
2) Have you tried using ManualResetEvent (.WaitOne() and .Set() instead?)
3) In your event, if invoke is required, you are re-calling your event again. Confusing...
4) Are you supposed to wait until the other thread is done? Then Thread.Join()?