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.
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
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.
My “form1” is just a simple page with buttons that launch different forms which do all the work, the "form1" code for the first four buttons is below.
What I want is for each form to run in a separate thread.
Public Class Main
Private Sub btnDownLoadStockPrices_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDownLoadStockPrices.Click
LoadStocksFromDownloadSite.Visible = True
End Sub
Private Sub btnLoadOptionsIntoDatabase_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadOptionsIntoDatabase.Click
LoadOptionsIntoDatabase.Visible = True
End Sub
Private Sub btnVerifyDatabases_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnVerifyDatabases.Click
VerifyDatabase.Visible = True
End Sub
Private Sub btnAnalyzeStock_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAnalyzeStock.Click
AnalyzeSingleStock.visible = True
End Sub
End Class
I’ve found plenty of code examples to have different buttons on a single form run a subroutine in a separate thread, but for some reason I can’t seem to apply it to an entire form.
I think it’s something simple, like I need to tie each new thread as a handle to each forms “load” routine, but I just can’t get it to work. I don’t care about “synchronizing” threads at all, as each form is really a completely different functional program.
Any help would be much appriciated!
This isn't very common; generally it's best to limit all UI stuff to a single thread. But if you're convinced that you need each form to run on a separate thread, you must take into account the Windows API event handling model. The [over]-simplified version is that each form must have its own message loop to remove event messages from the queue and process them, so if you want to open a form on a new thread, you need to create that message pump.
The easiest way to do that is using the Application.Run method, and let the .NET Framework handle creating that message pump for you. For example:
Dim frm As Form1 = New Form1()
Application.Run(frm)
From looking at the code shown in your question, I can't discern any possible reason why those forms would need to run on separate threads. You can call the Show method of multiple forms so that they will be displayed on the screen at the same time. They won't block each other as long as you don't use the ShowDialog method, which displays each as a modal dialog. This is the way so many applications display multiple toolbox windows and other kinds of forms on the screen at the same time.
If you need to do some type of processor-intensive calculation, you still don't need to run each on a separate thread. Spin up a background thread (the BackgroundWorker class makes this very simple) and update the appropriate form's UI using the Invoke method.
You can certainly do this on Win32 but I don't know how well this maps over to .net.
The essential issue is that window handles have thread affinity. So you really need all interaction with them to happen in that thread. Essentially this means that you create all the window handles associated with that form in its thread. You also need to run a message loop in the thread.
The reason that people usually run all the UI out of the main thread and handle long-running actions in separate threads is that it is easier that way. You should ask yourself again why you want to do it this non-standard way.
I suspect you are not quite seeing the full picture. The need for threads in a desktop app principally arises when you have long running actions. Usually you want to keep your UI responsive and providing feedback for the long running action. Doing so leads to threads.
However, in your proposed solution you now have a multitude of extra threads and complexity, and you are still faced with the original problem! A long running action on one of your forms will hang it unless you perform that action in a separate thread, and once again we reach the standard solution to the problem.
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
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