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
Related
I'm updating an existing application that scans barcodes and is written in VB.net running on windows compact framework 3.5. The scanner is a POCKETPC running windows mobile handheld 6.5. I have added code that uses Asynchronous TCP sockets in a class module. The sockets code is reading and sending data to and from a buffer pool. I now need to “inform” the GUI form that data has been received from the TCP socket and is ready for processing. Because the two processes are running on different threads I realise I cannot access the GUI controls directly. I therefore create a windows message (WM_CUSTOMMSG = &H400) and then use “SENDMESSAGE”. There is an existing WndProc sub (Protected Overrides Sub WndProc(ByRef msg As Microsoft.WindowsCE.Forms.Message)) that handles the WM_DECODEDATA for the scanner message. I added in code to now also process the WM_CUSTOMMSG message I am creating. The WM_CUSTOMMSG is arriving at the WndProc and I am able to display a MessageBox and write a log file, but any changes made to the GUI controls just disappear. I tried to start a forms timer but this also has no effect. Code for the WM_DECODEDATA message updates the GUI controls perfectly. What am I missing / done wrong?
Public Class frmHome
Public SockReceiveMsg As Microsoft.WindowsCE.Forms.Message
Public Sub New()
Private yy As Integer = 0
Private xx As Integer = 0
InitializeComponent()
Me.MsgWin = New MsgWindow(Me)
' Add any initialization after the InitializeComponent() call.
SockReceiveMsg = Microsoft.WindowsCE.Forms.Message.Create(MsgWin.Hwnd, MsgWindow.WM_CUSTOMMSG, New IntPtr(xx), New IntPtr(yy))
end class
Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
'This is the async call back sub
MessageWindow.SendMessage(frmHome.SockReceiveMsg)
end sub
Protected Overrides Sub WndProc(ByRef msg As Microsoft.WindowsCE.Forms.Message)
Dim rc As Integer
Dim ar() As String
If msg.Msg = WM_CUSTOMMSG Then
Try
MsgBox("restart timer") 'this displays
Reader.ReaderEngineAPI.Beeper(8, "") 'a quick ok beep. this works
frmHome.timer1.Enabled = False
frmHome.timer1.Interval = 100
frmHome.timer1.Enabled = True
Catch ex As Exception
MsgBox("wndproc Error1: " & ex.Message)
End Try
End If
'pass all messages onto the base processing. Here the windows ones get processed and our ones get cleared and the storage released
MyBase.WndProc(msg)
End Sub
I don't know if/what you're doing wrong, but you can probably do things alot easier. All Controls (including Forms) have an Invoke() method that you can call to let the framework deal with the windows messages.
This article describes it in a bit more detail, including the InvokeRequired property, which you can probably ignore if you know the data is sent from another thread.
If you do choose to handle these messages manually (since you already have the WndProc routine), be sure to catch all exceptions in the method that updates the GUI, and perhaps inspect the InvokeRequired to see if the control agrees that you can update it from that thread.
I would go with a delegate and an eventhandler on the thread code and use InvokeRequired with a custom delegate to update the GUI thread.
Handling custom message is not recommended. Or why does MS hide WndProc in WindowsCE.Forms? OTOH you may need to Refresh the controls that have been changed to let them know that they need to update. Do you use PostMessage or SendMessage. If later, that would block the thread code until the message is processed, which may block the GUI to update itself, if the WndProc itself uses SendMessage inside the code to handle the custom message.
Private Sub UpdateTextBox(ByVal sender As Object, ByVal e As EventArgs)
'---delegate to update the textbox control
txtMessagesArchive.Text += str
End Sub
That would be called from a background thread in the same class via:
Me.Invoke(New EventHandler(AddressOf UpdateTextBox))
Different ways to update GUI from background thread, but in C#
Thank you to all who provided answers. I had previously tried a delegate with invoke but this caused the system to crash. I spent over a week trying to find out what was wrong with the delegate code – I mean it is so simple, but was not successful. I then tried the WndProc route. Josef’s example in his answer showed me that the delegate must be in the same class as the backgound thread. I had placed the delegate in the form class. Once I put the delegate in the correct class it works perfectly. I have 50 years of experience in IT and programming and you can still learn.
I am not going to try to get to the bottom of the WndProc problem. This is now academic as the delegate route is a better solution and is now working. Thank you
Ok I'm pretty new to using threads but so far I've managed to get the following:
Private Delegate Sub dlgUpdateText(text as string)
Private Sub UpdateStatus(text as string)
If rtxStatus.InvokeRequired then
Dim dlg as new dlgUpdateText(AddressOf UpdateStatus)
Me.Invoke(dlg, text)
Else
rtxStatus.text = text
End If
End Sub
and from my Async BackgroundWorker I call
UpdateStatus("Some text")
which seems to work ok however in my original code (which generates errors because I'm updating the control from the wrong thread) I used the following code to append to the (rich)textbox:
rtxStatus.Select(rtxStatus.TextLength, 0)
rtxStatus.SelectionColor = Color.Red
rtxStatus.AppendText("Some error occurred gathering data")
My question is how should I modify my new code to allow me to do this rather than just replace the text? I have read several guides on using delegates but I'm still lost on a few points so I don't really know what's going on with the code I have.
Bonus questions (which probably serve best to show what needs explaining to me!):
What does the Delegate Sub actually do? It doesn't seem to serve any purpose other than hold the property (text) that was already passed to the main Sub (UpdateStatus)?
What is happening when Me.Invoke is called? Me is the current form so when I pass the Delegate Sub and the text where is it specified that the text should be passed to the rtxSTatus.Text property?
UpdateStatus runs in the main thread (?) and is called from one of the background worker threads so why is the Invoke even necessary or is the UpdateStatus Sub run under the background thread that called it? When I altered the text that is applied in the Else statement to see which was run it seems that Invoke is never used to change the text in the box.
Any help would be really appreciated, I'm completely baffled by this - thanks!
Rather than creating a delegate I would suggest using the existing methods offered from a backgroundworker. The backgroundworker provides two methods to access the main thread:
The ProgressChanged event to update the main thread during backgroundworker processing and the RunWorkerCompleted event to update the main thread once the backgroundworker process is complete.
You can find this information and how to implement it from the following link:
http://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
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.
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.
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