2010-01-03 40 views
3

我创建不使用以下方法http://pietschsoft.com/post/2008/07/C-Generate-WebPage-Thumbmail-Screenshot-Image.aspx如何在工作线程上创建和使用WebBrowser控件?

我试图使应用程序的多线程网站截屏的应用程序,但我遇到了以下错误:

[ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.]

任何建议如何解决这个问题?我的代码基本如下:

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

的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; 
} 
+0

我有点困惑,你试图在非GUI线程内创建和操作浏览器控件。你有没有可能将繁重的工作与控制权的交互分离开来,将繁重的工作委托给工作者线程? – Tormod 2010-01-03 18:02:03

+0

以及我试图做到这一点,但我卡住了。 – Karim 2010-01-03 18:19:01

回答

4

尝试设置该线程的ApartmentState托管浏览器控制:

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

这个工程。但我需要将ApartmentState设置为STA而不是MTA。 thread.SetApartmentState(ApartmentState.STA); – Karim 2010-01-03 18:49:04

0

改变是否从STAThreadMain方法的属性MTAThread帮助?

实施例:

[STAThread] 
public static void Main() 
{ 

变为:

[MTAThread] 
public static void Main() 
{ 
+1

不,它仍然崩溃,同样的错误 – Karim 2010-01-03 17:52:33

6

web浏览器,像许多ActiveX控件,具有严格的线程要求。创建它的线程必须使用Thread.SetApartmentState()来初始化以将其切换到STA。线程必须抽取一个消息循环,你从Application.Run()中获得一个。

这使得浏览器相当棘手。这里是让你开始的代码。请注意Completed回调在后台线程上运行。不要忘记调用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); 
    } 
    } 
} 
+0

相关的问题,特别是这个答案我正面临一个类似的问题,并写了一个.NET WebBrowser的包装来抽象掉消息循环问题。对于对.NET非常简单的无头浏览器感兴趣的其他人,我将代码发布到GitHub并通过nuget提供,以获取更多信息,请参阅https://github.com/LeastOne/WebBrowserWaiter – LeastOne 2014-11-01 06:35:22

+0

检查页面底部许可条款。 – 2014-11-01 10:11:55

+0

@LeastOne我在你的github上提出了问题,你可以请看看。 – jkyadav 2016-10-28 05:43:18

相关问题