首先的问题是需要“阻塞” UI 线程,直到某个事件被触发。通常可以重构应用程序以使用异步事件处理程序(带或不带async/await
),以将执行控制权交还给消息循环并避免任何阻塞。
现在让我们说,由于某种原因你不能重构你的代码。在这种情况下,您需要一个辅助模式消息循环。您还需要在等待事件时禁用主 UI,以避免令人讨厌的重新进入场景。等待本身应该是用户友好的(例如,使用等待光标或进度动画)并且不忙(避免在紧密循环中消耗 CPU 周期DoEvents
)。
一种方法是使用带有用户友好消息的模式对话框,当所需的 JavaScript 事件/回调发生时,该消息会自动关闭。这是一个完整的例子:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WbTest
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IScripting))]
public partial class MainForm : Form, IScripting
{
WebBrowser _webBrowser;
Action _onScriptInitialized;
public MainForm()
{
InitializeComponent();
_webBrowser = new WebBrowser();
_webBrowser.Dock = DockStyle.Fill;
_webBrowser.ObjectForScripting = this;
this.Controls.Add(_webBrowser);
this.Shown += MainForm_Shown;
}
void MainForm_Shown(object sender, EventArgs e)
{
var dialog = new Form
{
Width = 100,
Height = 50,
StartPosition = FormStartPosition.CenterParent,
ShowIcon = false,
ShowInTaskbar = false,
ControlBox = false,
FormBorderStyle = FormBorderStyle.FixedSingle
};
dialog.Controls.Add(new Label { Text = "Please wait..." });
dialog.Load += (_, __) => _webBrowser.DocumentText =
"<script>setTimeout(function() { window.external.OnScriptInitialized}, 2000)</script>";
var canClose = false;
dialog.FormClosing += (_, args) =>
args.Cancel = !canClose;
_onScriptInitialized = () => { canClose = true; dialog.Close(); };
Application.UseWaitCursor = true;
try
{
dialog.ShowDialog();
}
finally
{
Application.UseWaitCursor = false;
}
MessageBox.Show("Initialized!");
}
// IScripting
public void OnScriptInitialized()
{
_onScriptInitialized();
}
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IScripting
{
void OnScriptInitialized();
}
}
看起来像这样:

另一种选择(用户不太友好的选择)是使用类似WaitOneAndPump
from here的东西。您仍然需要注意禁用主 UI 并向用户显示某种等待反馈。
更新以解决评论。您的 WebBrowser 实际上是 UI 的一部分并且对用户可见吗?用户应该能够与之交互吗?如果是这样,您就不能使用辅助线程来执行 JavaScript。您需要在主线程上执行此操作并继续发送消息,但WaitOne
不会发送大部分 Windows 消息(它只发送一小部分与 COM 相关的消息)。您也许可以使用WaitOneAndPump
我上面提到的。您仍然需要在等待时禁用 UI,以避免重新进入。
无论如何,这仍然是一个kludge。您真的不应该仅仅为了保持线性代码流而阻止执行。如果你不能使用async/await
,你总是可以实现一个简单的状态机类并使用回调从它离开的地方继续。以前是这样的async/await
。