5

I want to make 3 threads that each run the WebBroswer control. So I would like to use the ThreadPool to make things easy.

for(int i = 0;i < 3;i++)
{
   ThreadPool.QueueUserWorkItem(new WaitCallback(gotoWork), i));
}
WaitAll(waitHandles);

....../

void gotoWork(object o)
{
   string url = String.Empty;
   int num = (int)o;
   switch(num)
   {
     case 0:
       url = "google.com";
       break;
     case 1:
       url = "yahoo.com";
       break;
     case 2:
       url = "bing.com";
       break;
   }
   WebBrowser w = new WebBrower();
   w.Navigate(url);
}

But I get an error saying that I need a STA thread which the ThreadPool will never be. Before trying this method I tried this.

Thread[] threads = Thread[3];
for(int i = 0;i < 3;i++)
{
    threads[i] = new Thread(new ParameterizedStart(gotoWork);
    threads[i] = SetApartmentState(ApartmentState.STA); //whoo hoo
    threads[i] = Start();
}
for(int i = 0; i < 3;i++)
{
    threads[i].Join();
}

And the WebBrowsers all initialized and everything looks good but only one more two will actually do anything and its never consistant at all. Threading has been such a nightmare. Can anybody suggest a nice alternative?

Proximo
  • 6,235
  • 11
  • 49
  • 67
  • Why do you want to load multiple pages at once? Do you need a WebBrowser or would a HttpWebRequest work (e.g. do you need to see the page and/or do you need JavaScript on the page to run)? If you're looking to do web crawling/scraping, there are more efficient ways. – Chad Jun 12 '12 at 03:43

4 Answers4

0

You need to host webbrowser control in somewhere to get it work (add it to a form), secondly you should use Invoke() of the host control (or a similar delegate) such as Form.Invoke() to interact with WebBrowser control.

No need to remind now but yes, your threads should be STA

dr. evil
  • 26,944
  • 33
  • 131
  • 201
0

You can use such type of class for your purpose to host WebBrowser control:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SomeNameSpace
{
    public class WebForm : Form
    {
        public WebBrowser WebBrowser { get; set; }

        public WebForm()
        {
            WebBrowser = new WebBrowser();
        }
    }
}
0

After all I choose this solution: http://watin.sourceforge.net/documentation.html

In your case you should do this:

...
Thread thread = new Thread(SomeMethod);
thread.SetApartmentState(ApartmentState.STA);
...
0
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");
}
takrl
  • 6,356
  • 3
  • 60
  • 69
Mian Ghous
  • 34
  • 1
  • does this throw each Navigate into a thread and continue executing? If so does each there need to be a new Tester object for each thread? – xendi Jan 18 '15 at 15:04