DoEvents is used yet application is not responsive - vb.net

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.

Related

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.

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.

VB.NET DOEVENTS

I have a VB6 app, which does a lot of processing in the form_load. A call to DoEvents ensures that the form loads before the processing is complete. However,this does not appear to work in VB.NET i.e. in the following code, the loop finishes befoRe the form is loaded, even though I have called DOEVENTS:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For i = 0 To 100000
Application.DoEvents()
Dim Test As String = "Test"
Next
End Sub
What have I missed?
Your statement:
A call to DoEvents ensures that the form loads before the processing is complete
To the best of my knowledge, this is NOT true. The form "paints" before processing your load event - the processing (that is loading) is still not complete. All that is ensured by a call to DoEvents is OTHER messages get a chance to be handled when you are in the middle of a long processing. MSDN's help of DoEvent() describes it as:
Processes all Windows messages currently in the message queue.
Also, it specifically states:
Unlike Visual Basic 6.0, the DoEvents method does not call the Thread.Sleep method.
I believe it might be risky for you to handle your requirement in the Load event. Just a search for "Application.DoEvents in load" in google talks about bad experiences for many. I suggest you can explore handling your requirement in Shown event.
DoEvents in VB.NET should be actively avoided.
Have a look at DoEvents in .NET and Stop DoEvents and DoEvents is Evil to see why.
It is a common misconception that this does the same as the VB6 DoEvents. In almost all cases what you want to achieve can be done in some other way in .NET without the need to call DoEvents.
Your best bet is probably a BackgroundWorker object have a look at this example to get you started.
A point to note though is that you can't update controls on your form from a background worker without using a delegate - but that is another question...
You should use a BackgroundWorker component in order to do lots of background work without freezing up the user interface in VB.Net. Look at this MSDN tutorial How to run an operation in the background

Does DoEvents effect only the current thread?

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.

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