Does DoEvents effect only the current thread? - vb.net

I have a simple 'Working' form that runs on its own thread to keep the user informed that the application hasn't died during long running operations. In order to get the working form to update I had to insert a DoEvents() call.
I'm curious, will this only pump messages for the current thread I'm in, or will it do it for the whole application? I would prefer that the main window stay unresponsive till the operation finishes, so I'm curious as to the behavior. Below is the code for the working form.
Just to be clear, I'm fine with the code I have, but I would like to know how DoEvents() behaves with threads.
Public Class frmWorking
''' <summary>
''' Creates and starts a new thread to handle the Working Dialog
''' </summary>
''' <returns>The thread of the Working dialog.</returns>
''' <remarks></remarks>
Public Shared Function StartWait() As WorkingFromToken
Dim th As New Threading.Thread(AddressOf ShowWait)
Dim token As New WorkingFromToken
th.Start(token)
Return token
End Function
Private Shared Sub ShowWait(token As WorkingFromToken)
Dim frm As New frmWorking
Try
frm.Show()
Do
If frm.txtWait.Text.Length > 45 Then
frm.txtWait.Text = "Working"
Else
frm.txtWait.Text &= "."
End If
Windows.Forms.Application.DoEvents()
Threading.Thread.Sleep(250)
Loop While token.Running
frm.Hide()
Catch ex As Threading.ThreadAbortException
Threading.Thread.ResetAbort()
frm.Hide()
Return
End Try
End Sub
End Class

DoEvents will only pump the current UI thread.
However, I do not recommend your approach.
Instead, you should do your work on a background thread, and show a modal progress form on the UI thread and update it using BeginInvoke or a BackgroundWorker.

DoEvents will only effect the thread from which it is called. It will dequeue all windows messages posted to that thread and dispatch them accordingly. After all messages have been dispatched it will return back to the caller.
I have a couple of other observations about your code though.
You have basically created your own crippled version of a message loop by calling DoEvents repeatedly in a loop. It would be better to just call Application.Run to initiate a full blown message loop.
Creating a message loop on a thread other than the main UI thread is rarely a good idea. There are some weird things that occur that are hard to deal with. For example, a modal dialog box from one thread could overlap a modal dialog box from another.
Attempting to catch a ThreadAbortException is pointless in most situation. If you ever get this exception then it is possible (perhaps even likely) that the state of the entire AppDomain has been corrupted. It is better to tear down the application domain than to try to gracefully deal with it. This is because the exception could be injected at any point during the execution of the thread and those injection points could be in the middle or a write, a lengthy operation, or otherwise some unsafe point.
As a corollary to the point above do not use Thread.Abort to terminate another thread. There are many too many things that can go wrong. It is better to cause the thread to end gracefully using safer mechanisms.

Related

DoEvents is used yet application is not responsive

In my application, I call a process to update software - which is stored within its own class. Even thou I have been writing Application.DoEvents() in few places for some reason the label in the update form is not updating and the form itself is inactive.
Namespace software
Public Class updater
Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean
Application.DoEvents()
Thread.Sleep(1000)
frmUpdate.lblResult.Text = "Update is about to begin"
Thread.Sleep(1000)
frmUpdate.lblResult.Text = "Downloading data"
Thread.Sleep(1000)
Application.DoEvents()
frmUpdate.lblResult.Text = "About to start the writing process"
Application.DoEvents()
frmUpdate.lblResult.Text = "Software was updated, please restart your device."
End Function
End Class
End Namespace
I can't figure out why you were calling DoEvents in those specific locations, since none of them will have any visible effect where they are. The first one happens before any labels are changed, so allowing the form to refresh there is pointless. The others are at the very end, after all the long-running work is already done (the three sleeps). So, while they will allow the form to refresh before execution leaves the method, it will very very shortly be leaving the method anyway, so there's no point in doing it there either. The only place where it would even be applicable to call DoEvents would be between two long running things. For instance, if you did this, you'd notice a difference:
Public Function UpdateSoftware(ByVal url As String, ByVal downloadFolder As String) As Boolean
Thread.Sleep(1000)
frmUpdate.lblResult.Text = "Update is about to begin"
Application.DoEvents()
Thread.Sleep(1000)
frmUpdate.lblResult.Text = "Downloading data"
Application.DoEvents()
Thread.Sleep(1000)
frmUpdate.lblResult.Text = "About to start the writing process"
frmUpdate.lblResult.Text = "Software was updated, please restart your device."
End Function
You need to understand, in .NET WinForms (as well as in WPF), the UI is running on a single thread. What I mean by that is, if one of your event handlers contains code that takes a long time to complete, the UI will be frozen for the entire time that event handler is executing. The UI refreshing is completely blocked until the last event handler finishes doing whatever it was doing. DoEvents is somewhat of a hack way of getting around that (and a dangerous hack, at that). Each time you call DoEvents, it returns control back to the form to handle whatever else it has queued up to do (such as repainting the screen and handling user input) and then execution is returned to the original event handler so it can continue where it left off. That means, each time you call DoEvents, it allows the form to repaint at that point, but the event handler still blocks the UI in between all of the DoEvents.
As others have already implied, using DoEvents is highly discouraged. Not only is it less effective, it can cause all sorts of unexpected behavior. In pre-.NET versions of VB (e.g. VB6), DoEvents was often the only option, but in .NET, multi-threading is relatively easy. There may be occasions where DoEvents is legitimately still useful, but they should be very few and far between and only implemented with great care and caution.
There are two recommended ways of implementing multi-threading in WinForm applications. The original method, which still works well, is to use a BackgroundWorker component (you'll find it in the WinForm designer tool-box). The BackgroundWorker raises an event on a different thread so that you can do all your long-running work in that event handler, without it blocking the UI. Then, when it's all done, it raises another event, back on the UI thread so that you can update the UI after the work is complete.
The newer method, which is much cleaner and easier to read, but a little more sophisticated, is to use the Async and Await keywords to make all your long-running methods, and your event handler that calls them, asynchronous.

Using a thread with designer objects vb.net

I have a big problem. I am currently designing an Antivirus, and it is coming along very well. But having all the scanning engines running on the same thread, I.E. the main one, is causing the app to lag in loading, and to become unresponsive during processes. I have tried implementing multithreading to increase the speed and overall performance of my application. But, every time that I try, i get the error of cross threading, I.E. I cannot use the form designers progress bars, buttons and labels etc. I just want to know why this error is thrown up, and how to fix it.
Thanks in Advance!
Use InvokeRequired to check which thread you are calling from, if you're not in the UI thread then InvokeRequired is True, and so you can invoke a delegate from the UI Thread to safely alter the Control:
Public Sub SetText(ByVal text As String)
If (Me.InvokeRequired) Then
'Invoke a delegate from the UI Thread
Me.Invoke(DirectCast(Sub() Label1.Text = "Test", MethodInvoker))
Else
Button1.Text = text
End If
End Sub
It is unsafe to call a control from a thread other than the one that created the control without using the Invoke method. Take a look at this example: http://msdn.microsoft.com/library/ms171728%28v=vs.110%29.aspx
Set your form property: CheckForIllegalCrossThreadCalls to false. The you are getting no errors any more.
Furthermore you must show, that you get a reference between your Thread and your form Controls. Otherwise you change the Controls of the Thread handled window (which you don't see).
Try to write Methods or Functions with By Ref parameter, to share your controls.

Abort running code on form when form is disposed?

I have a large, complex client application I am now maintaining. For security purposes, it requires a constant connection to the server. It has a thread that is handling all the socket communication between itself and the server.
The way it's currently written, if there is any communication issue with the server, the communications thread fires off an event that closes and disposes all open forms and returns the user back to the initial connection/logon screen.
The problem I'm having is that sometimes this communication issue can happen in the middle of the execution of a function (such as one that is blocked by a modal form). When the modal form and parent form are disposed of, the function still finishes running, often leading to exceptions and errors.
For example, the report form has a function that opens a dialog, accepts input, and then runs a report based on that input:
'Inside the class for the ReportForm:
Private Sub RunReport()
'Run code that requests list of reports from server
_ReportSelectionForm = New frmReportSelection(reportList)
_ReportSelectionForm.ShowInTaskbar = False
Me.AddOwnedForm(_ReportSelectionForm)
_ReportSelectionForm.ShowDialog(Me)
'the following code will still execute when ReportForm (Me) is disposed:
username = _ReportSelectionForm.txtUsername
If (_ReportSelectionForm.DialogResult = Windows.Forms.DialogResult.Ok) Then
'Run code
ElseIf (_ReportSelectionForm.DialogResult = Windows.Forms.DialogResult.Cancel) Then
'Run different code
End If
'etc
End Sub
So, if the Report Selection Form is open and the communications thread times out communications with the server, the communications error event fires which closes and disposes the ReportForm. This, in turn, closes the _ReportSelectionForm Dialog. When this happens, even though the parent form has been disposed, finishes running the code after "_ReportSelectionForm.ShowDialog(Me)". This throws an exception on "_ReportSelectionForm.DialogResult" or "_ReportSelectionForm.txtUsername" because _ReportSelectionForm is Nothing.
If this was one isolated place, I could handle this with a few extra checks before continuing to run the function, but it's all over this large program.
What's the best way to handle this? Can I abort code execution on a form that I'm closing?
Hopefully I explained it adequately. My Google-Fu is failing me. Thanks in advance.
Change your code to:
Dim result as DialogResult = _ReportSelectionForm.ShowDialog(Me)
If (result = Windows.Forms.DialogResult.Ok) Then
'Run code
ElseIf (result = Windows.Forms.DialogResult.Cancel) Then
'Run different code
End If
That way you're not referencing the ReportSelectionForm.

Update control asynchronously

I've been reading over the internet how to solve the problem of updating controls from different threads than the one that contains the control and also read "Pro VB 2010 and the .Net 4.0 Platform" and I start to wonder. Is the following code performing in synchronous fashion?
Private Sub SaveData(ByVal filepath As String)
If InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf SaveData))
Else
... the actual code
End If
End Sub
It depends widely on your calling context.
If you are calling it from the UI thread or your main thread, then yes it will function synchronously.
If you call it from a thread that is not your UI or main thread, then it is going to function asynchronously to your UI or main thread, but synchronously with the calling thread, waiting until the UI or main thread is done processing the delegate.
So it can go both ways. It can operate synchronously and asynchronously. I think what you are missing is that the code itself does not preform in a synchronous or asynchronous fashion, the execution of the code is either synchronous or asynchronous.
The reason why you make an update function:
Private Sub UpdateLabel(byval tLabel as Label, byval Value as String)
If tLabel.Invokerequired Then
Me.Invoke(New MethodInvoker(AddressOf UpdateLabel)
Else
tLabel.Text = Value
End If
End Sub
Is so that you can call it whenever you want to change the text, without having to worry about a cross thread exception.
This method would be safe to call from the UI thread during a click event and it would also be safe to call from a along running background thread, because the change would still only ever be made on the UI or Main thread. You use code like this to ensure synchronicity with your changes to controls owned by other threads.
This style of updating is meant to synchronize your changes and avoid cross threading.
The Invoke method is a synchronous call that will block the calling thread until the delegate finishes executing on the UI thread.
If you want to calling thread to continue immediately (before the code on the UI thread runs), call BeginInvoke(), which is asynchronous.
I know this question is a little old, but here's a modification of Pow-Ian's answer that worked for me.
Private Sub UpdateLabel(ByVal tControl As Control, ByVal Value As String)
If tControl.InvokeRequired Then
tControl.Invoke(Sub() tControl.Text = Value)
Else
tControl.Text = Value
End If
End Sub
This should work to change the text for any control using the UI thread, even if it is called from an async method.

running vb.net in a new thread

how do I force a particular set of vb.net codes to run in a new thread or process.?
Edit 1: I am trying TTS in vb.net but whenever click the play button , The whole program freezes and I cannot do anything else until the speech is over
In a comment below you mention the library you are using and that changes this whole answer.
Answer to your problem:
In your case since you are using the SAPI.SpVoice library you don't need to do any work related to spinning up background threads and such since that object support asynchronous playback. Check out the arguments of the Speak method. Here is the documentation: http://msdn.microsoft.com/en-us/library/ms723609(v=vs.85).aspx
Answer to your question as it is posed:
The simplest method is to use a background worker process to run some code. This will allow your program to run some long process and not block the UI thread. The background worker even provides events to notify your UI thread of it's progress.
Here is an link to MSDN http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
The background worker is a simple way to spin off some work onto another thread, there are other tools for more complex scenarios. In essence you create an instance of a background worker object and add your long-running code to a function that is created to handle it's DoWork event. There are ProgressChanged and RunWorkerCompleted events that should be handled as well. There are methods that can be called to cancel the process. The MSDN link above provides a couple of good complete code examples.
Wrap the "set of codes" into a method and dump it onto the ThreadPool
ThreadPool.QueueUserWorkItem(AddressOf MyMethod)
the ThreadPool suggestion worked for me for a WP7 Silverlight app:
Private Sub AddAnagrams()
ClearAnagramsList()
UpdateAnagramsCount() 'update the count first, then add the items
ShowCalculating(True)
ThreadPool.QueueUserWorkItem(AddressOf UpdateAnagramsOnUIthread)
End Sub
Private Sub UpdateAnagramsOnUIthread()
Dispatcher.BeginInvoke(AddressOf UpdateAnagrams)
End Sub
Private Sub UpdateAnagrams()
ListAnagrams.ItemsSource = _Combinator.CombinedItems 'this is a virtualized datasource
ShowCalculating(False)
End Sub
Private Sub ShowCalculating(ByVal flag As Boolean)
LblCalculating.Visibility = If(flag, Windows.Visibility.Visible, Windows.Visibility.Collapsed)
End Sub