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

本文关键字:线程 活动 WebBrowser 事件 | 更新日期: 2023-09-27 18:20:23

我有一个带有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();

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

必须调用Application.Run()。这不仅是为了确保线程不会过早结束,还需要让WebBrowser引发其事件。使用消息循环是一种标准的方式,在这种方式中,像WebBrowser这样的重线程组件可以确保其事件在创建对象的同一线程上引发。它实现了STA合同。

MessageBox.Show()在引擎盖下使用的消息循环使自己成为模态,这就是为什么在使用MessageBox时它现在可以工作的原因。与Application.Run()实现的消息循环没有本质区别。

使用Application.ExitThread()使线程结束。它必须在调用Application.Run()的同一线程上调用。当您在DocumentCompleted事件处理程序中调用它时,这不会是一个问题。

主要问题是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();

如果您需要等待来自其他线程的事件,有很多方法可以进行同步。TaskCompletionSource类是一种同时传递数据并具有良好的基于Task的接口的简单方法。例如,如果我想异步地等待文档的标题,它很简单:

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

当然,这假设被调用者是一个异步方法,但您可以对该任务执行许多其他操作,例如,注册一个continuation。

您不需要保留对Thread实例的引用——启动的线程是根线程,因此它们永远不会被收集。

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

使用标志。在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) { }