Webbrowser Control in a New Thread

WebBrowser Control in a new thread

You have to create an STA thread that pumps a message loop. That's the only hospitable environment for an ActiveX component like WebBrowser. You won't get the DocumentCompleted event otherwise. Some sample code:

private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}

void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}

How might I create and use a WebBrowser control on a worker thread?

Try setting the ApartmentState of the thread hosting the browser control:

var thread = new Thread(objThreadStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

MultiThreading WebBrowser Control C# STA

public sealed class SiteHelper : Form
{
public WebBrowser mBrowser = new WebBrowser();
ManualResetEvent mStart;
public event CompletedCallback Completed;
public SiteHelper(ManualResetEvent start)
{
mBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(mBrowser_DocumentCompleted);
mStart = start;
}
void mBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Generated completed event
Completed(mBrowser);
}
public void Navigate(string url)
{
// Start navigating
this.BeginInvoke(new Action(() => mBrowser.Navigate(url)));
}
public void Terminate()
{
// Shutdown form and message loop
this.Invoke(new Action(() => this.Close()));
}
protected override void SetVisibleCore(bool value)
{
if (!IsHandleCreated)
{
// First-time init, create handle and wait for message pump to run
this.CreateHandle();
this.BeginInvoke(new Action(() => mStart.Set()));
}
// Keep form hidden
value = false;
base.SetVisibleCore(value);
}
}

An other class as

public abstract class SiteManager : IDisposable
{
private ManualResetEvent mStart;
private SiteHelper mSyncProvider;
public event CompletedCallback Completed;

public SiteManager()
{
// Start the thread, wait for it to initialize
mStart = new ManualResetEvent(false);
Thread t = new Thread(startPump);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
mStart.WaitOne();
}
public void Dispose()
{
// Shutdown message loop and thread
mSyncProvider.Terminate();
}
public void Navigate(string url)
{
// Start navigating to a URL
mSyncProvider.Navigate(url);
}
public void mSyncProvider_Completed(WebBrowser wb)
{
// Navigation completed, raise event
CompletedCallback handler = Completed;
if (handler != null)
{
handler(wb);
}
}
private void startPump()
{
// Start the message loop
mSyncProvider = new SiteHelper(mStart);
mSyncProvider.Completed += mSyncProvider_Completed;
Application.Run(mSyncProvider);
}
}


class Tester :SiteManager
{
public Tester()
{
SiteEventArgs ar = new SiteEventArgs("MeSite");

base.Completed += new CompletedCallback(Tester_Completed);
}

void Tester_Completed(WebBrowser wb)
{
MessageBox.Show("Tester");
if( wb.DocumentTitle == "Hi")

base.mSyncProvider_Completed(wb);
}

//protected override void mSyncProvider_Completed(WebBrowser wb)
//{
// // MessageBox.Show("overload Tester");
// //base.mSyncProvider_Completed(wb, ar);
//}
}

On your main form:

private void button1_Click(object sender, EventArgs e)
{
//Tester pump = new Tester();
//pump.Completed += new CompletedCallback(pump_Completed);
//pump.Navigate("www.cnn.com");

Tester pump2 = new Tester();
pump2.Completed += new CompletedCallback(pump_Completed);
pump2.Navigate("www.google.com");
}

WebBrowser Threads don't seem to be closing

WebBrowser is specifically designed to be used from inside a windows forms project. It is not designed to be used from outside a windows forms project.

Among other things, it is specifically designed to use an application loop, which would exist in pretty much any desktop GUI application. You don't have this, and this is of course causing problems for you because the browser leverages this for its event based style of programming.

A quick word to any future readers who happen to be reading this and which are actually creating a winforms, WPF, or other application that already has a message loop. Do not apply the following code. You should only ever have one message loop in your application. Creating several is setting yourself up for a nightmare.

Since you have no application loop you need to create a new application loop, specify some code to run within that application loop, allow it to pump messages, and then tear it down when you have gotten your result.

public static string GetGeneratedHTML(string url)
{
string result = null;
ThreadStart pumpMessages = () =>
{
EventHandler idleHandler = null;
idleHandler = (s, e) =>
{
Application.Idle -= idleHandler;

WebBrowser wb = new WebBrowser();
wb.DocumentCompleted += (s2, e2) =>
{
result = wb.Document.Body.InnerHtml;
wb.Dispose();
Application.Exit();
};
wb.Navigate(url);
};
Application.Idle += idleHandler;
Application.Run();
};
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
pumpMessages();
else
{
Thread t = new Thread(pumpMessages);
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
return result;
}

Getting an error when creating a WebBrowser in a new thread vb.net

You get the error because the new thread(s) that you create are generally in a so called Multithreaded Apartment (MTA). According to the error the WebBrowser control can only be created on a thread that is in a Single-threaded Apartment (STA, an example being the UI thread).

Changing your thread's apartment is not recommended because it must be MTA in order to be multithreaded alongside of the UI thread.

This leaves you with pretty much only one option: create and modify the control on the UI thread only, and do the other heavy lifting in your new thread.

I would create the control on the UI thread, and in the end of the thread I would perform invocation to run the navigating code.

Solution for .NET Framework 4.0 or higher:

In your loop:

Dim decible = New WebBrowser()
decible.Name = "browser" & nr
web_panel.Controls.Add(decible)

trd = New Thread(AddressOf make_it)
trd.IsBackground = True
trd.Start(decible) 'Passing the web browser as a parameter.

The make_it method:

Private Sub make_it(ByVal decible As WebBrowser)
Dim RandomIpAddress As String = user_text(GetRandom(user_text.Length))
MsgBox(RandomIpAddress)

ChangeUserAgent(RandomIpAddress)

If Me.InvokeRequired = True Then
Me.Invoke(Sub() decible.Navigate("http://www.whatsmyuseragent.com/"))
Else
decible.Navigate("http://www.whatsmyuseragent.com/")
End If

End Sub

Solution for .NET Framework 3.5 or lower:

In your loop (this is the same as above):

Dim decible = New WebBrowser()
decible.Name = "browser" & nr
web_panel.Controls.Add(decible)

trd = New Thread(AddressOf make_it)
trd.IsBackground = True
trd.Start(decible) 'Passing the web browser as a parameter.

The make_it method:

Private Sub make_it(ByVal decible As WebBrowser)
Dim RandomIpAddress As String = user_text(GetRandom(user_text.Length))
MsgBox(RandomIpAddress)

ChangeUserAgent(RandomIpAddress)

NavigateWebBrowser(decible, "http://www.whatsmyuseragent.com/")

End Sub

Delegate Sub NavigateDelegate(ByVal Browser As WebBrowser, ByVal Url As String)

Private Sub NavigateWebBrowser(ByVal Browser As WebBrowser, ByVal Url As String)
If Me.InvokeRequired = True Then
Me.Invoke(New NavigateDelegate(AddressOf NavigateWebBrowser), Browser, Url)
Else
Browser.Navigate(Url)
End If
End Sub


Related Topics



Leave a reply



Submit