0

我面临一个Application.DoEvents()可以解决的问题。问题是 WebBrowser 假设异步导航到一个 url 但它没有,当我使用 Application.DoEvents() 它解决了这个问题,我认为这是因为应用程序处理一些其他事件并且不传递事件导航正确。

我阅读了一些有关此方法的信息,并且了解该方法将导致应用程序处理所有当前事件。现在我有点担心,因为我用大炮杀死了一只蚂蚁,有人能告诉我我所做的是否值得吗?

4

2 回答 2

4

是的,Application.DoEvents() 解决了这个问题。核心问题是 WebBrowser 的核心是一个重度线程组件。您可以调用它的 Navigate() 方法,它会在不阻塞您的代码的情况下完成它的工作,该方法几乎立即返回。

然而,问题是在某些时候它必须运行您的 DocumentCompleted 事件。保证在您创建浏览器对象的线程上运行。这很难做到,您的线程很可能正忙于做其他事情。就像坐在一个循环中一样,测试 ReadyState 属性。没有机制可以中断此循环并运行事件处理程序。

所以你看到的是 ReadyState 属性永远不会改变, DocumentCompleted 事件永远不会触发。这称为死锁,是线程代码的一个非常常见的诅咒。使用 DoEvents 是后门,即“泵送消息循环”。它允许浏览器闯入您的线程并触发事件。这反过来会更新 ReadyState 属性并让您跳出循环。

然而,DoEvents 有一个大问题。它不是选择性的,它不仅限于处理允许事件触发的消息。它还会发送其他通知,这些通知会使您的程序崩溃。就像您的用户对缓慢的网站感到不耐烦并关闭您的表单一样。这会破坏浏览器对象,但不会停止您的循环。您现在正在测试已处置浏览器的 ReadyState 属性。轰隆隆!

您需要以不同的方式执行此操作。循环阻塞或挂起 UI 线程是不合法的,很容易造成死锁。实际上,Microsoft 指南禁止STA 线程。解决方法很简单,将等待循环之后的任何代码移动到 DocumentCompleted 事件处理程序。您可能需要向您的类添加一些状态变量,以便您知道该事件表示特定网页的完成或用户不再对结果感兴趣。

于 2012-10-14T12:07:39.997 回答
1

Application.Dovents()方法处理所有待处理的消息。这可能导致:

  1. 在当前代码块完成之前输入代码块两次。(假设您通过单击按钮浏览浏览器。用户单击按钮,而您的代码正在等待浏览器完成用户再次单击。在这种情况下,Application.Doevents()将导致在执行下一行之前处理该方法。)

  2. 中断关键代码。(假设您有一个耗时的方法并且用户单击了关闭按钮。您的表单将消失但您的代码将继续运行。一个真正的问题。

  3. 更多意想不到的结果。

但是我觉得有时使用这种方法是必要的,并且像 webbrowser 这样的简单解决方案很难在多线程中使用(尤其是当它可见时)。如果您必须使用此方法,您应该确保用户和其他事物(计时器、按钮、事件与)不会中断任何事情。详细讨论:Application.DoEvents() 的使用

于 2012-10-14T11:35:20.063 回答