WebView2 のスレッド モデル

サポートされているプラットフォーム: Win32、Windowsフォーム、WinUi、WPF。

WebView2 コントロールはコンポーネント オブジェクト モデル (COM) に基づいており、単一スレッド アパートメント (STA) スレッドで実行する必要 があります。

スレッド セーフティ

WebView2 は、メッセージ ポンプを使用する UI スレッド上に作成する必要があります。 すべてのコールバックは、そのスレッドで発生し、WebView2 への要求は、そのスレッドで実行する必要があります。 別のスレッドから WebView2 を使用しても安全ではありません。

唯一の例外は、 の Content プロパティです CoreWebView2WebResourceRequest 。 プロパティ Content ストリームは、バックグラウンド スレッドから読み取ります。 UI スレッドのパフォーマンス低下を防ぐために、ストリームはアジャイルである必要があります。またはバックグラウンド STA から作成する必要があります。

注意

オブジェクト プロパティはシングル スレッドです。 たとえば、成功するスレッド以外のスレッドからの呼び出し (つまり、Cookie が返されます)。ただし、このような呼び出しの後に Cookie のプロパティ CoreWebView2CookieManager.GetCookiesAsync(null) Main (など) にアクセスしようとすると、例外がスローされます c.Domain

Reentrancy

イベント ハンドラーや完了ハンドラーを含むコールバックは、シリアルに実行されます。 イベント ハンドラーを実行してメッセージ ループを開始した後、イベント ハンドラーまたは完了コールバックを再入可能な方法で実行することはできません。 WebView2 アプリが、WebView イベント ハンドラー内で、入れ子になったメッセージ ループまたはモーダル 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 WebView when web message is received.
      form.ShowDialog(); // This will cause a reentrancy issue and cause the newly created WebView 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 アプリのネイティブ コード デバッグを有効にする必要があります。

  1. WebView2 プロジェクトを開きます。Visual Studio。
  2. ソリューション エクスプローラーで、WebView2プロジェクトを右クリックし、[プロパティ] を 選択します
  3. [デバッグ ] タブを選択し、**** 次に示すように[ネイティブ コードデバッグを有効にする] チェック ボックスをオンにします。

ネイティブ コード のデバッグを有効Visual Studio

遅延

一部の WebView2 イベントは、関連するイベント引数に設定された値を読み取り、イベント ハンドラーの完了後にいくつかのアクションを開始します。 イベント ハンドラーなどの非同期操作も実行する必要がある場合は、関連付けられたイベントのイベント引数でメソッド GetDeferral を使用します。 返されるオブジェクトは、イベント ハンドラーが要求されるまで、イベント ハンドラーが完全と見 Deferral Complete なされません Deferral

たとえば、イベント ハンドラーの完了時に、子ウィンドウとして接続する a を提供するためにイベント NewWindowRequested CoreWebView2 を使用できます。 ただし、非同期的に作成する必要がある場合 CoreWebView2 は、 でメソッド GetDeferral を呼び出す必要があります NewWindowRequestedEventArgs 。 非同期的に作成し、プロパティを設定した後、 メソッドによって返されるオブジェクトを CoreWebView2 NewWindow 呼び NewWindowRequestedEventArgs Complete DeferralGetDeferral します。

C の遅延#

アプリ内で a をC#、ブロックと一緒 Deferral に使用するのがベスト プラクティス using です。 ブロック using は、ブロックの中央に例外がスローされた場合でも、確実に完了 Deferral using します。 その代わりに、明示的に呼び出すコードがあるが、呼び出しが発生する前に例外がスローされる場合、ガベージ コレクターが最終的に遅延を収集して破棄するまで延期は完了しません。 Complete Complete その間、WebView2 はアプリ コードがイベントを処理するのを待機します。

たとえば、呼び出す前に例外がある場合、イベントは "処理" とは見なされません Complete 。WebView2 が Web コンテンツのレンダリングをブロックします。 WebResourceRequested

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 スレッドのメッセージ ポンプに依存して、イベント ハンドラー コールバックと非同期メソッド完了コールバックを実行します。 メッセージ ポンプをブロックするメソッド (など) を使用する場合、WebView2 イベント ハンドラーと非同期メソッド完了ハンドラーは Task.Result WaitForSingleObject 実行されません。 たとえば、次のコードは完了しないので、完了するまでメッセージ ポンプを Task.Result ExecuteScriptAsync 停止します。 メッセージ ポンプがブロックされたため、完了 ExecuteScriptAsync できない。

たとえば、次のコードは、 を使用するために機能しません Task.Result

private void Button_Click(object sender, EventArgs e)
{
    string result = webView2Control.CoreWebView2.ExecuteScriptAsync("'test'").Result;
    MessageBox.Show(this, result, "Script Result");
}

代わりに、メッセージ ポンプや UI スレッドをブロックしない await async and などの非同期 await メカニズムを使用します。 次に、例を示します。

private async void Button_Click(object sender, EventArgs e)
{
    string result = await webView2Control.CoreWebView2.ExecuteScriptAsync("'test'");
    MessageBox.Show(this, result, "Script Result");
}

関連項目