WebView2 應用程式的執行緒模型
支援的平臺:Win32、Windows Forms、WinUI、WPF。
WebView2 控制項是以 元件物件模型 (COM) 為基礎,而且必須在 單一執行緒 Apartment (STA) 執行緒上執行。
執行緒安全性
WebView2 必須在使用訊息幫浦的 UI 執行緒上建立。 所有回呼都會發生在該執行緒上,而對 WebView2 的要求必須在該執行緒上完成。 從另一個執行緒使用 WebView2 並不安全。
唯一的例外是 Content
的 CoreWebView2WebResourceRequest
屬性。 屬性資料 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 應用程式的原生程式碼偵錯,如下所示:
- 在 Visual Studio 中開啟您的 WebView2 專案。
- 在方案總管中,以滑鼠右鍵按一下 WebView2 專案,然後選取 [屬性]。
- 選取 [ 偵錯] 索引 標籤,然後選取 [ 啟用原生程式碼偵錯] 複 選框,如下所示。
延期
某些 WebView2 事件會讀取在相關事件引數上設定的值,或在事件處理常式完成之後啟動一些動作。 如果您也需要執行非同步作業,例如事件處理常式,請在相關聯事件的事件引數上使用 GetDeferral
方法。 傳回 Deferral
的 物件可確保在要求 的 方法 Deferral
之前 Complete
,不會將事件處理常式視為完成。
例如,您可以使用 NewWindowRequested
事件來提供 , CoreWebView2
以在事件處理常式完成時以子視窗的方式連線。 但是,如果您需要以非同步方式建立 CoreWebView2
,您應該在 上 NewWindowRequestedEventArgs
呼叫 GetDeferral
方法。 在您以非同步方式建立 CoreWebView2
並在 上設定 NewWindow
屬性之後,請在 方法所傳 GetDeferral
回的 物件上 Deferral
NewWindowRequestedEventArgs
呼叫 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 執行緒的訊息幫浦來執行事件處理常式回呼和非同步方法完成回呼。 如果您使用封鎖訊息幫浦的方法,例如 Task.Result
或 WaitForSingleObject
,則 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
機制,例如 async
和 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 參考