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

限于喜欢 提交于 2019-11-28 00:27:22
Darin Dimitrov

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

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

WebBrowser, like many ActiveX controls, has strict threading requirements. The thread that creates it must be initialized with Thread.SetApartmentState() to switch it to STA. And the thread must pump a message loop, you get one from Application.Run().

That makes talking to the browser pretty tricky. Here's code to get you started. Beware that the Completed callback runs on a background thread. Don't forget to call Dispose() to shut down the thread.

using System;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;

class WebPagePump : IDisposable {
  public delegate void CompletedCallback(WebBrowser wb);
  private ManualResetEvent mStart;
  private SyncHelper mSyncProvider;
  public event CompletedCallback Completed;

  public WebPagePump() {
    // 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(Uri url) {
    // Start navigating to a URL
    mSyncProvider.Navigate(url); 
  }
  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 SyncHelper(mStart);
    mSyncProvider.Completed += mSyncProvider_Completed;
    Application.Run(mSyncProvider);
  }
  class SyncHelper : Form {
    WebBrowser mBrowser = new WebBrowser();
    ManualResetEvent mStart;
    public event CompletedCallback Completed;
    public SyncHelper(ManualResetEvent start) {
      mBrowser.DocumentCompleted += mBrowser_DocumentCompleted;
      mStart = start;
    }
    public void Navigate(Uri url) {
      // Start navigating
      this.BeginInvoke(new Action(() => mBrowser.Navigate(url)));
    }
    void mBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
      // Generated completed event
      Completed(mBrowser);
    }
    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);
    }
  }
}

Does changing the attribute on your Main method from STAThread to MTAThread help?

Example:

[STAThread]
public static void Main()
{

changes to:

[MTAThread]
public static void Main()
{
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!