WebView2 アプリのスレッド モデル

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

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

スレッド セーフ

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

唯一の例外は の Content プロパティです CoreWebView2WebResourceRequestContentプロパティ ストリームは、バックグラウンド スレッドから読み取られます。 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 アプリのネイティブ コード デバッグを有効にする必要があります。

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

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

遅延

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

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

C での遅延#

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

たとえば、 を呼び出すCompleteWebResourceRequested前に例外が発生した場合、イベントは "処理" とは見なされないため、次の操作を行わないでください。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 例のように ブロックを使用します。 ブロックは usingDeferral 例外があるかどうかに関係なく、 が完了することを保証します。

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");
}

代わりに、 や などのasyncawait非同期awaitメカニズムを使用します。これは、メッセージ ポンプや UI スレッドをブロックしません。 例:

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

関連項目