WebView2 アプリのスレッド モデル
サポートされているプラットフォーム: Win32、Windows フォーム、WinUI、WPF。
WebView2 コントロールは コンポーネント オブジェクト モデル (COM) に基づいており、 シングル スレッド アパートメント (STA) スレッドで実行する必要があります。
スレッド セーフ
WebView2 は、メッセージ ポンプを使用する UI スレッドに作成する必要があります。 すべてのコールバックはそのスレッドで行われ、WebView2 への要求はそのスレッドで行う必要があります。 別のスレッドから WebView2 を使用しても安全ではありません。
唯一の例外は の Content
プロパティです CoreWebView2WebResourceRequest
。
Content
プロパティ ストリームは、バックグラウンド スレッドから読み取られます。 UI スレッドのパフォーマンス低下を防ぐために、ストリームはアジャイルであるか、バックグラウンド STA から作成する必要があります。
オブジェクト プロパティはシングル スレッドです。 たとえば、 以外のMain
スレッドからの呼び出しCoreWebView2CookieManager.GetCookiesAsync(null)
は成功します (つまり、Cookie が返されます)。ただし、そのような呼び出しの後に Cookie のプロパティ (などc.Domain
) にアクセスしようとすると、例外がスローされます。
再 入
イベント ハンドラーや完了ハンドラーを含むコールバックは、シリアル実行されます。 イベント ハンドラーを実行してメッセージ ループを開始した後、イベント ハンドラーまたは完了コールバックを再入可能な方法で実行することはできません。 WebView2 アプリが WebView2 イベント ハンドラー内で入れ子になったメッセージ ループまたはモーダル UI を同期的に作成しようとすると、この方法によって再入が試行されます。 このような再入は WebView2 ではサポートされていないため、イベント ハンドラーはスタック内に無期限に残されます。
たとえば、次のコーディング方法はサポートされていません。
private void Btn_Click(object sender, EventArgs e)
{
// Post web message when button is clicked
this.webView2Control.ExecuteScriptAsync("window.chrome.webview.postMessage(\"Open Dialog\");");
}
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
{
string msg = e.TryGetWebMessageAsString();
if (msg == "Open Dialog")
{
Form1 form = new Form1(); // Create a new form that contains a new WebView2 instance when web message is received.
form.ShowDialog(); // This will cause a reentrancy issue and cause the newly created WebView2 control inside the modal dialog to hang.
}
}
代わりに、次のコードに示すように、イベント ハンドラーの完了後に適切な作業をスケジュールします。
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
{
string msg = e.TryGetWebMessageAsString();
if (msg == "Open Dialog")
{
// Show a modal dialog after the current event handler is completed, to avoid potential reentrancy caused by running a nested message loop in the WebView2 event handler.
System.Threading.SynchronizationContext.Current.Post((_) => {
Form1 form = new Form1();
form.ShowDialog();
form.Closed();
}, null);
}
}
注:
WinForms アプリと WPF アプリの場合、デバッグ目的で完全な呼び出し履歴を取得するには、次のように WebView2 アプリのネイティブ コード デバッグを有効にする必要があります。
- Visual Studio で WebView2 プロジェクトを開きます。
- ソリューション エクスプローラーで、WebView2 プロジェクトを右クリックし、[プロパティ] を選択します。
- [ デバッグ ] タブを選択し、次に示すように [ ネイティブ コード デバッグを有効にする ] チェック ボックスをオンにします。
遅延
一部の WebView2 イベントは、関連するイベント引数に設定された値を読み取るか、イベント ハンドラーの完了後に何らかのアクションを開始します。 イベント ハンドラーなどの非同期操作も実行する必要がある場合は、関連付けられているイベントのイベント引数で メソッドを使用 GetDeferral
します。 返されたDeferral
オブジェクトは、 の メソッドDeferral
が要求されるまでComplete
、イベント ハンドラーが完全と見なされないようにします。
たとえば、 イベントを NewWindowRequested
使用して、 を指定 CoreWebView2
して、イベント ハンドラーが完了したときに子ウィンドウとして接続できます。 ただし、 を非同期的に作成CoreWebView2
する必要がある場合は、 で NewWindowRequestedEventArgs
メソッドをGetDeferral
呼び出す必要があります。 を非同期的に作成CoreWebView2
し、 で プロパティNewWindowRequestedEventArgs
をNewWindow
設定した後、 メソッドによって返される オブジェクトに対Deferral
して をGetDeferral
呼び出Complete
します。
C での遅延#
C# で を Deferral
使用する場合は、ブロックと共 using
に 使用することをお勧めします。 ブロックは using
、ブロックの Deferral
途中で例外がスローされた場合でも、 が完了することを保証します using
。 代わりに、 を明示的に呼び出すコードがありますが、呼び出 Complete
しが発生する前に Complete
例外がスローされた場合、ガベージ コレクターが遅延を最終的に収集して破棄するまで、遅延は完了しません。 その間、WebView2 はアプリ コードがイベントを処理するのを待機します。
たとえば、 を呼び出すComplete
WebResourceRequested
前に例外が発生した場合、イベントは "処理" とは見なされないため、次の操作を行わないでください。WebView2 がその Web コンテンツのレンダリングをブロックします。
private async void WebView2WebResourceRequestedHandler(CoreWebView2 sender,
CoreWebView2WebResourceRequestedEventArgs eventArgs)
{
var deferral = eventArgs.GetDeferral();
args.Response = await CreateResponse(eventArgs);
// Calling Complete is not recommended, because if CreateResponse
// throws an exception, the deferral isn't completed.
deferral.Complete();
}
代わりに、次の using
例のように ブロックを使用します。 ブロックは using
、 Deferral
例外があるかどうかに関係なく、 が完了することを保証します。
private async void WebView2WebResourceRequestedHandler(CoreWebView2 sender,
CoreWebView2WebResourceRequestedEventArgs eventArgs)
{
// The using block ensures that the deferral is completed, regardless of
// whether there's an exception.
using (eventArgs.GetDeferral())
{
args.Response = await CreateResponse(eventArgs);
}
}
UI スレッドをブロックする
WebView2 は、UI スレッドのメッセージ ポンプに依存して、イベント ハンドラー コールバックと非同期メソッド完了コールバックを実行します。 や WaitForSingleObject
などのTask.Result
メッセージ ポンプをブロックするメソッドを使用する場合、WebView2 イベント ハンドラーと非同期メソッド完了ハンドラーは実行されません。 たとえば、次のコードは完了しません。これは、完了を待機している間にメッセージ ポンプをExecuteScriptAsync
停止するためTask.Result
です。 メッセージ ポンプがブロックされているため、 ExecuteScriptAsync
を完了できません。
たとえば、次のコードは を使用 Task.Result
するため、機能しません。
private void Button_Click(object sender, EventArgs e)
{
string result = webView2Control.CoreWebView2.ExecuteScriptAsync("'test'").Result;
MessageBox.Show(this, result, "Script Result");
}
代わりに、 や などのasync
await
非同期await
メカニズムを使用します。これは、メッセージ ポンプや UI スレッドをブロックしません。 例:
private async void Button_Click(object sender, EventArgs e)
{
string result = await webView2Control.CoreWebView2.ExecuteScriptAsync("'test'");
MessageBox.Show(this, result, "Script Result");
}
関連項目
- WebView2 の概要
- WebView2Samples リポジトリ - WebView2 機能の包括的な例。
- WebView2 API リファレンス