简体   繁体   English

我如何在工作线程上创建和使用WebBrowser控件?

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

I am creating an application that does screen shots of websites using the following method http://pietschsoft.com/post/2008/07/C-Generate-WebPage-Thumbmail-Screenshot-Image.aspx 我正在创建一个应用程序,使用以下方法对网站进行屏幕截图http://pietschsoft.com/post/2008/07/C-Generate-WebPage-Thumbmail-Screenshot-Image.aspx

I tried to make the application multithreaded but I have run into the following error: 我试图使应用程序多线程,但我遇到了以下错误:

[ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.] [ActiveX控件'8856f961-340a-11d0-a96b-00c04fd705a2'无法实例化,因为当前线程不在单线程单元中。

Any suggestions how to fix this issue? 有任何建议如何解决这个问题? My code is basically as follows: 我的代码基本如下:

List<string> lststrWebSites = new List<string>();
lststrWebSites.Add("http://stackoverflow.com");
lststrWebSites.Add("http://www.cnn.com");
foreach (string strWebSite in lststrWebSites)
{
  System.Threading.ThreadStart objThreadStart = delegate
  {
    Bitmap bmpScreen = GenerateScreenshot(strWebSite, -1, -1);
    bmpScreen.Save(@"C:\" + strWebSite + ".png", 
      System.Drawing.Imaging.ImageFormat.Png);
  };
  new System.Threading.Thread(objThreadStart).Start();
}

The GenerateScreenShot() function implementation is copied from the above URL: 从上面的URL复制GenerateScreenShot()函数实现:

public Bitmap GenerateScreenshot(string url)
{
  // This method gets a screenshot of the webpage
  // rendered at its full size (height and width)
  return GenerateScreenshot(url, -1, -1);
}

public Bitmap GenerateScreenshot(string url, int width, int height)
{
  // Load the webpage into a WebBrowser control
  WebBrowser wb = new WebBrowser();
  wb.ScrollBarsEnabled = false;
  wb.ScriptErrorsSuppressed = true;
  wb.Navigate(url);
  while (wb.ReadyState != WebBrowserReadyState.Complete) 
    { Application.DoEvents(); }


  // Set the size of the WebBrowser control
  wb.Width = width;
  wb.Height = height;

  if (width == -1)
  {
    // Take Screenshot of the web pages full width
    wb.Width = wb.Document.Body.ScrollRectangle.Width;
  }

  if (height == -1)
  {
    // Take Screenshot of the web pages full height
    wb.Height = wb.Document.Body.ScrollRectangle.Height;
  }

  // Get a Bitmap representation of the webpage as it's rendered in 
  // the WebBrowser control
  Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
  wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
  wb.Dispose();

  return bitmap;
} 

WebBrowser, like many ActiveX controls, has strict threading requirements. 与许多ActiveX控件一样,WebBrowser具有严格的线程要求。 The thread that creates it must be initialized with Thread.SetApartmentState() to switch it to STA. 必须使用Thread.SetApartmentState()初始化创建它的线程,以将其切换到STA。 And the thread must pump a message loop, you get one from Application.Run(). 并且线程必须泵出一个消息循环,你从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. 不要忘记调用Dispose()来关闭线程。

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);
    }
  }
}

Try setting the ApartmentState of the thread hosting the browser control: 尝试设置托管浏览器控件的线程的ApartmentState

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

Does changing the attribute on your Main method from STAThread to MTAThread help? Main方法的属性从STAThread更改为MTAThread帮助?

Example: 例:

[STAThread]
public static void Main()
{

changes to: 更改为:

[MTAThread]
public static void Main()
{

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从backgroundWorker线程创建通用控件? - How might I create a generic control from a backgroundWorker thread? 在托管WebBrowser控件的应用程序中,如何在由WebBrowser控件查看的页面中调用JavaScript函数? - How might I call a JavaScript Function in a Page Viewed by a WebBrowser Control from the Application Hosting the WebBrowser Control? 如何删除单独的工作线程 function 并创建一个线程 - How can I remove seperate worker thread function and create a thread 无法在从后台工作程序加载的表单中使用WebBrowser控件? - Unable to use WebBrowser Control inside a form that is Loaded from background Worker? 新线程中的WebBrowser控件 - WebBrowser Control in a new thread 如何在C#WebBrowser控件中使用JQuery - How to use JQuery with the C# WebBrowser control 在单独的线程上创建WebBrowser控件 - Creating WebBrowser Control on a separate thread 将CookieContainer与WebBrowser控件一起使用? - Use CookieContainer with WebBrowser control? 如何在可以在Webbrowser控件中显示的HTML中创建“标签式”外观? - How do I create a “tabbed” look in a single piece of HTML that I can display in a webbrowser control? 如何在webbrowser控件中启用javascript? - How do I enable javascript in webbrowser control?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM