如何在WebBrowser事件触发之前保持线程处于活动状态

时间:2015-11-02 13:07:25

标签: c# multithreading

我有一个WebBrowser实例的线程,我附加了一个DocumentCompleted事件。但是根据我的观察,我的事件没有被提出,因为线程在它发生之前就结束了。当我将MessageBox.Show放在线程的末尾时,它会为事件提供时间。但是如何在没有MessageBox的情况下让线程等待呢?

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

4 个答案:

答案 0 :(得分:3)

必须调用Application.Run()。不仅要确保您的线程不会太快结束,还需要让WebBrowser提升其事件。使用消息循环是一种标准方法,其中大量线程组件(如WebBrowser)确保在创建对象的同一线程上引发其事件。它实现了STA合同。

MessageBox.Show()在底层使用的消息循环使自己成为模态,这就是为什么它在你使用MessageBox时正常工作的原因。与Application.Run()实现的消息循环没有本质上的区别。

使用Application.ExitThread()来结束线程。必须在调用Application.Run()的同一线程上调用它。在DocumentCompleted事件处理程序中执行此操作时,这不会成为问题。

答案 1 :(得分: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实例的引用 - 启动的线程是根,因此永远不会收集它们。

答案 2 :(得分:0)

如何在DocumentCompleted事件处理程序中设置标志,然后等待您正在执行MessageBox.Show()的位置设置标志?

答案 3 :(得分: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) { }