如何在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();
您必须调用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) { }