VB.NET Invalid cross-thread access - vb.net

I have a background worker control on a form.
In this form I have another form that shows a progress:
Private _fWait As frmWait
I am updating this form, change its label to tell the user what is currently going on.
When the background worker is finished, I want to close this form _fWait.
I am using
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'do the background worker stuff. I have not stated it here because it is not important
_fWait.Close()
_bDone = True
End Sub
But I am getting the error "Invalid cross-thread access: The access to the control frmWait occured from a diffent thread than the thread it was created by." on the line "_fWait.Close()".
Can somebody tell me what I did wrong?
Thank you!

When you call _fWait.Close() is has to be called on the UI thread whereas the BackgroundWorker1_DoWork handler will be running on a different thread which is why you are getting the error. There are two options for closing the form:
1) Continue to close the form on the DoWork handler but marshal the request onto the UI thread using something like the code below:
this.Invoke(() => _fWait.Close());
It's been a while since I've done VB, so you'll have to convert the C# code into VB...sorry.
2) Handle the RunWorkerCompleted event and close it there. If you BackgroundWorker was started on
the UI thread, then the RunWorkerCompleted will also be called on the UI thread.
If I remember right the ProgressedChanged event will also be called on the UI assuming that the BackgroundWorker was created on the UI thread. If the BackgroundWorker is created another another thread, then the RunWorkerCompleted and ProgressChanged will also be called on a separate thread and you will have to marshal calls to the UI as described above in step 1.

In simple programs, you can ignore cross-thread errors by adding this to your Form.Load method:
CheckForIllegalCrossThreadCalls = False
Note that it can cause problems when running multiple asynchronous threads that access shared data, so don't use it indiscriminately.
If multiple threads cause a shared routine to run, use SyncLock to prevent multiple simultaneous instances.

Related

Backgroundworker - UI still not responsive until BGW completed

I implemented a BGW to my application in hopes of speeding up loading time for the starting window(40+ controls)
I will not post my whole code as it's far too long but will give you the gist of the code. I split big function calls that take time to complete alongwith a handful of controls and moved them into the BGW in hopes of asyncronously loading controls to help quicken the process.
It is understood that I have to move UI changing code to the ProgressChanged event or RunWorkerCompleted event, which I have done. I originally had all code thrown into the DoWork event and it was extremely fast but found out it's not safe so I had reworked it to move all UI-related oode to the ProgressChanged. It's not nearly as fast now and it seems that the BGW controls wait until the UI thread completes prior to changing the controls in the BGW_ProgressCHanged event. I never saw this 'lag' between the two when I had all the changes in DoWork. What can I do about this? Or can I at least had the BGW update the controls realtime rather than waiting for all controls are completed before updating all controls?
The responsiveness is lower as well as it locks up the window to wait for the BGW controls to update. Just looking for what could possibly be happening
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
SyncLock <globalVar>
BackgroundWorker1.ReportProgress(0, "Tom")
End SyncLock
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim value As String = DirectCast(e.UserState, String)
Select Case e.ProgressPercentage
Case 0
lblName.text = value
lblName.Visible = true
End Select
End Sub
You removed all evidence of the problem in your code, but the diagnostic is an excellent match. You have a fire hose problem. Your code is calling ReportProgress far too often.
Things go wrong when your ProgressChanged event handler needs more time than the time between ReportProgress calls. Which is like drinking from a fire hose, no matter how fast you swallow the water, you just can't keep up with the flow.
Which is what the UI thread experiences. When it finishes the call to your ProgressChanged event handler, there's yet more water, yet another request to call the handler. That relentlessly continues without the UI thread ever being able to keep up. It now doesn't get around to doing its normal duties anymore. Which means that your UI stops updating, paints are no longer performed. And it doesn't respond to input anymore.
This can last for a while after the worker thread stopped running, the UI is still trying to work down the backlog of requests. When it finally gets there, the UI suddenly springs back to life.
A simple way to diagnose this condition is to add this method call after the ReportProgress call:
System.Threading.Thread.Sleep(45)
Which slows down the worker thread enough to limit the number of ReportProgress() calls to no more than 21 per second. Which is plenty fast enough for human eyes, anything faster just looks like a blur so is wasted effort. If that fixes the problem then you found the cause.
Using Sleep() like this is otherwise an ugly Q&D solution for the problem, it of course also slows down your worker so its gets less work done. You'll have to improve your code so that this doesn't happen and just makes less ReportProgress calls.
One thing you might want to add before starting the worker:
Me.SuspendLayout()
Then, in the RunWorkerCompleted event:
Me.ResumeLayout()
That should suspend all layout logic until all the work is done, then update the entire form in 1 operation.
EDIT
Replace
BackgroundWorker1.ReportProgress(...)
With
DirectCast(sender, BackgroundWorker).ReportProgress(...)
And get rid of the synclock.
EDIT2
The correct way to feed the data to the DoWork event is through the DoWorkEventArgs.
Start work like this:
BackgroundWorker1.RunWorkerAsync(<globalvar>)
And access it like this:
Dim globalVarData As Object = e.Argument

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

Progress "bar" using threads in vb.net

I currently have a program that runs several "intense" queries. I added a textbox and presented status updates when a query was starting, eding and how many were left. This would suite my need, but the textbox doesn't actually display anything until all the queries are finished. It then displays all the updates at once. I'm assuming updating the textbox in another thread would solve this issue, and that is where I am lost. How can I use a thread that receives a message from the main form running the query and have it display it in the textbox?
The BackgroundWorker component suits your need (sample code is there in the MSDN link). You handle its DoWork event and perform the actual query in it. You report the progress by calling its ReportProgress method. To display the reported progress, you should handle its ProgressChanged event and update the UI. You start the job by calling the RunWorkerAsync method of the background worker. Using BackgroundWorker relieves you from manually starting and stopping threads and communicating with the UI thread to update the progress bar.
BackgroundWorker is a good general-purpose method for doing intensive work on a background thread. But, since your question sounds like you are doing database operations, it might be easier to use the native support for asynchronous operations in ADO.Net. You could use callbacks for the progress bar.
The easiest way to do this is making use of BackgroundWorker, handling it's DoWork event and reporting progress to the ProgressBar with ProgressChanged event.
to start:
worker.RunAsync()
report progress:
worker.ReportProgress(10) 'for 10%
Adding to what Mehrdad and Alex posted, here's how to handle the event raised by the ReportProgess method (ProgressChanged) to update the progress bar:
Private Sub backgroundWorker_ProgressChanged ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles backgroundWorker.ProgressChanged
Me.progressBar1.Value = e.ProgressPercentage
End Sub

Creating a webbrowser control in a background thread in backgroundworker

Is it possible to create a WebBrowser control in a background thread in BackgroundWorker?
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Using web1 As New WebBrowser
End Using
End Sub
This throws the following error:
ActiveX control
'8856f961-340a-11d0-a96b-00c04fd705a2'
cannot be instantiated because the
current thread is not in a
single-threaded apartment.
Does anyone know a way to create a background WebBrowser? I would like to do it to extract info from websites then spit out the various HTML DOM arrays
Set the appartment state of your thread before the thread is started:
myThread.IsBackground = false;
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
The thread that the Webbrowser runs in must be ApartmentState.STA. From a BackgroundWorker thread you'll have to use Invoke on a UI object and add the Webbrowser in that context.
I don't think you really need to run the WebBrowser object in a background thread anyway. All of it's functionality is handled asynchronously with events.
You can also write a wrapper class, that instantiates the web browser control, create methods that wrap your extraction functions, and fire off the results through public events.
From here you just create a new instance of your wrapper, and add it to the ThreadPool.
Remember to do proper owner-thread invoking to avoid cross-thread exceptions ;)
Having been down both roads, I would recommend WatiN, an open source library to automate IE and Firefox. The WebBrowser control is good for displaying content, but for anything more complex it becomes a big hassle.