2015-11-02 29 views
0

我有一個WebBrowser實例的線程,並附加了一個DocumentCompleted事件。但從我觀察到的情況來看,我的事件並沒有發生,因爲線程在它發生之前就結束了。當我將MessageBox.Show放在線程的末尾時,它會給事件提出時間。但是如何讓線程在沒有MessageBox的情況下等待呢?如何保持線程活着,直到WebBrowser事件觸發

WebBrowser browser; 
     Thread t = new Thread(() => 
     { 
      string url = string.Format("webpage.com"); 
      browser = new WebBrowser(); 
      browser.ScriptErrorsSuppressed = true; 
      browser.Navigate(url); 
      browser.DocumentCompleted += browser_DocumentCompleted; 
      browser.DocumentCompleted += (o, a) => 
      { 
       //MessageBox.Show("In DocumentCompleted."); 
       List<Status> statusy = new List<Status>(); 
       IHTMLDocument2 currentDoc = (IHTMLDocument2)browser.Document.DomDocument; 
       //parsing the html doc 

       string Statuses = ""; 
       foreach (Status status in statusy) 
       { 
        Statuses += String.Format("{0} {1} - {2} --> {3}{4}", status.Date, status.Time, status.Centre, status.Message, Environment.NewLine); 
       } 
       MessageBox.Show(Statuses); 
      }; 

      MessageBox.Show("In thread!!!!"); 
     }); 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(); 
+0

你在使用什麼.net版本?它是一個控制檯應用程序? – Sievajet

+0

4.0,這個代碼來自控制檯應用程序,但是當它工作時它會在類文件中(在Windows窗體應用程序中,但沒有訪問實際的表單)。 – Dess

+0

您使用哪個MessageBox來等待? –

回答

2

的主要問題是,Web瀏覽器控件需要一個適當的消息循環的正常工作。您要啓動的線程沒有這樣的消息循環,所以Web瀏覽器無法真正做到多少。

最簡單的解決方案是簡單地將瀏覽器控件託管在一個表單中 - 這可以讓您輕鬆控制瀏覽器的生命週期,並且可以簡單地維護消息循環(這就是Application.Run的作用)。

如果這不適用於您(即您根本不想顯示任何表單),則需要創建一個無表單消息循環。使用你的代碼最簡單的例子:

WebBrowser browser; 

Thread t = new Thread(() => 
{ 
    string url = string.Format("google.com"); 
    browser = new WebBrowser(); 
    browser.ScriptErrorsSuppressed = true; 
    browser.Navigate(url); 
    browser.DocumentCompleted += (o, a) => 
    { 
    MessageBox.Show(browser.Document.Title); 

    Application.ExitThread(); 
    }; 

    Application.Run(); 
}); 
t.SetApartmentState(ApartmentState.STA); 
t.Start(); 

如果你需要等待從其他線程的情況下,有很多方式同步。一個簡單的方法同時傳遞數據,並且基於Task的接口是TaskCompletionSource類。例如,如果我要等待異步的文檔的標題,它是如此簡單:

WebBrowser browser; 
var tcs = new TaskCompletionSource<string>(); 

Thread t = new Thread(() => 
{ 
    string url = string.Format("google.com"); 
    browser = new WebBrowser(); 
    browser.ScriptErrorsSuppressed = true; 
    browser.Navigate(url); 
    browser.DocumentCompleted += (o, a) => 
    { 
    tcs.SetResult(browser.Document.Title); 

    Application.ExitThread(); 
    }; 

    Application.Run(); 
}); 
t.SetApartmentState(ApartmentState.STA); 
t.Start(); 

Console.WriteLine(await tcs.Task); 

。當然,這是假定被叫方是異步方法,但還有很多其他的事情可以做該任務 - 例如,註冊一個延續。

您不需要保留對Thread實例的引用 - 啓動的線程是根,因此它們將永遠不會被收集。

0

如何在DocumentCompleted事件處理程序設置一個標誌,然後等待要設置的標誌,你正在做的MessageBox.Show()

+0

事情是我無法做Thread.sleep(),因爲它會停止線程(並且事件不會觸發)。 – Dess

0

使用標誌。在DocumentCompleted事件處理程序中,將該標誌設置爲false。 然後使用像while語句:

bool Flag = true; 
browser.DocumentCompleted += (o, a) => 
     { 
      //MessageBox.Show("In DocumentCompleted."); 
      List<Status> statusy = new List<Status>(); 
      IHTMLDocument2 currentDoc = (IHTMLDocument2)browser.Document.DomDocument; 
      //parsing the html doc 

      string Statuses = ""; 
      foreach (Status status in statusy) 
      { 
       Statuses += String.Format("{0} {1} - {2} --> {3}{4}", status.Date, status.Time, status.Centre, status.Message, Environment.NewLine); 
      } 
      MessageBox.Show(Statuses); 
      Flag = false; 
     }; 
while (Flag) { } 
+0

不起作用:/我認爲它會。 – Dess

+0

您是否正確添加了此部分前面的代碼? –

+0

是的,線程仍然有效,但是flag永遠不會改變它的值。由於某些原因,事件不會啓動。 – Dess

3

必須調用Application.Run()。不僅要確保你的線程不會盡快結束,還需要讓WebBrowser提升其事件。使用消息循環是一種標準方式,其中大量線程化的組件(如WebBrowser)確保在創建該對象的同一線程上引發其事件。它執行STA合同。

MessageBox.Show()在引擎蓋下使用自己的模式的消息循環是爲什麼當你使用MessageBox時它正常工作。與Application.Run()實現的消息循環沒有本質區別。

使用Application.ExitThread()讓線程結束。它必須在調用Application.Run()的同一線程上調用。當您在DocumentCompleted事件處理程序中執行此操作時,這不會成爲問題。

+0

是的,我見過'MessageBox.Show'在編寫不好的應用程序時會造成無窮無盡的麻煩,理想情況是當涉及大量的Invoke調用時。 「看到了嗎?現在你的代碼是可重入的,接下來會發生什麼?沒人知道!」 – Luaan