共用方式為


WebView2 應用程式的執行緒模型

支援的平臺:Win32、Windows Forms、WinUI、WPF。

WebView2 控制項是以 元件物件模型 (COM) 為基礎,而且必須在 單一執行緒 Apartment (STA) 執行緒上執行。

執行緒安全性

WebView2 必須在使用訊息幫浦的 UI 執行緒上建立。 所有回呼都會發生在該執行緒上,而對 WebView2 的要求必須在該執行緒上完成。 從另一個執行緒使用 WebView2 並不安全。

唯一的例外是 ContentCoreWebView2WebResourceRequest 屬性。 屬性資料 Content 流是從背景執行緒讀取。 資料流程應該是敏捷式或應該從背景 STA 建立,以避免 UI 執行緒的效能降低。

物件屬性是單一執行緒。 例如,從 以外的 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 並在 上設定 NewWindow 屬性之後,請在 方法所傳 GetDeferral 回的 物件上 DeferralNewWindowRequestedEventArgs 呼叫 Complete

C 中的延遲#

在 C# 中使用 Deferral 時,最佳做法是搭配 區塊使用它 using 。 即使區塊中間 using 擲回例外狀況, Deferralusing 塊仍可確保 已完成 。 如果您有明確呼叫 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 執行緒的訊息幫浦來執行事件處理常式回呼和非同步方法完成回呼。 如果您使用封鎖訊息幫浦的方法,例如 Task.ResultWaitForSingleObject ,則 WebView2 事件處理常式和非同步方法完成處理常式不會執行。 例如,下列程式碼不會完成,因為 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");
}

請改用非同步 await 機制,例如 asyncawait ,這不會封鎖訊息幫浦或 UI 執行緒。 例如:

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

另請參閱