そもそもの問題は、何らかのイベントが発生するまで UI スレッドを「ブロック」する必要があることです。通常、アプリケーションをリファクタリングして非同期イベント ハンドラー ( の有無にかかわらずasync/await
) を使用し、実行制御をメッセージ ループに戻してブロックを回避することができます。
ここで、何らかの理由でコードをリファクタリングできないとしましょう。この場合、二次的なモーダル メッセージ ループが必要になります。また、厄介な再入シナリオを回避するために、イベントを待っている間はメイン UI を無効にする必要があります。待機自体は使いやすく (待機カーソルや進行アニメーションを使用するなど)、ビジーではないようにする必要があります ( DoEvents
.
これを行う 1 つの方法は、目的の 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 を無効にする必要があります。
とにかく、それはまだクラッジでしょう。線形のコード フローを維持するためだけに実行をブロックするべきではありません。を使用できない場合はasync/await
、単純なステート マシン クラスをいつでも実装し、コールバックを使用して、残った場所から続行できます。以前はそうだっasync/await
た。