2010-01-03 42 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

相關問題