Threadingmodell für WebView2-Apps
Unterstützte Plattformen: Win32, Windows Forms, WinUI, WPF.
Das WebView2-Steuerelement basiert auf com (Component Object Model) und muss in einem STA-Thread (Single Threaded Apartments) ausgeführt werden.
Threadsicherheit
WebView2 muss in einem UI-Thread erstellt werden, der eine Nachrichtenpumpe verwendet. Alle Rückrufe erfolgen in diesem Thread, und Anforderungen an WebView2 müssen für diesen Thread ausgeführt werden. Es ist nicht sicher, WebView2 aus einem anderen Thread zu verwenden.
Die einzige Ausnahme gilt für die Content
-Eigenschaft von CoreWebView2WebResourceRequest
. Der Content
Eigenschaftenstream wird aus einem Hintergrundthread gelesen. Der Stream sollte agil sein oder aus einem Hintergrund-STA erstellt werden, um eine Leistungsbeeinträchtigung des UI-Threads zu verhindern.
Objekteigenschaften sind singlethreaded. Beispielsweise wird der Aufruf CoreWebView2CookieManager.GetCookiesAsync(null)
von einem anderen Thread als Main
erfolgreich ausgeführt (d. h. Cookies werden zurückgegeben). Der Versuch, nach einem solchen Aufruf auf die Eigenschaften der Cookies (z c.Domain
. B. ) zuzugreifen, löst jedoch eine Ausnahme aus.
Reentranz
Rückrufe, einschließlich Ereignishandlern und Vervollständigungshandlern, werden seriell ausgeführt. Nachdem Sie einen Ereignishandler ausgeführt und eine Meldungsschleife gestartet haben, kann ein Ereignishandler- oder Vervollständigungsrückruf nicht erneut ausgeführt werden. Wenn eine WebView2-App versucht, synchron innerhalb eines WebView2-Ereignishandlers eine geschachtelte Nachrichtenschleife oder eine modale Benutzeroberfläche zu erstellen, führt dieser Ansatz zu einem Versuchten erneuten Eintritt. Eine solche ErneuteIntrehnung wird in WebView2 nicht unterstützt und würde den Ereignishandler auf unbestimmte Zeit im Stapel belassen.
Der folgende Codierungsansatz wird beispielsweise nicht unterstützt:
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.
}
}
Planen Sie stattdessen die entsprechenden Arbeiten, die nach Abschluss des Ereignishandlers ausgeführt werden sollen, wie im folgenden Code gezeigt:
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);
}
}
Hinweis
Für WinForms- und WPF-Apps müssen Sie das native Codedebuggen für WebView2-Apps wie folgt aktivieren, um die vollständige Aufrufliste zu Debugzwecken abzurufen:
- Öffnen Sie Ihr WebView2-Projekt in Visual Studio.
- Klicken Sie Projektmappen-Explorer mit der rechten Maustaste auf das WebView2-Projekt, und wählen Sie dann Eigenschaften aus.
- Wählen Sie die Registerkarte Debuggen und dann das Kontrollkästchen Debuggen mit nativem Code aktivieren aus, wie unten gezeigt.
Verzögerungen
Einige WebView2-Ereignisse lesen Werte, die für die zugehörigen Ereignisargumente festgelegt sind, oder starten eine Aktion, nachdem der Ereignishandler abgeschlossen ist. Wenn Sie auch einen asynchronen Vorgang ausführen müssen, z. B. einen Ereignishandler, verwenden Sie die GetDeferral
-Methode für die Ereignisargumente der zugeordneten Ereignisse. Das zurückgegebene Deferral
-Objekt stellt sicher, dass der Ereignishandler erst als abgeschlossen gilt, wenn die Complete
-Methode des Deferral
angefordert wird.
Für instance können Sie das NewWindowRequested
-Ereignis verwenden, um eine CoreWebView2
bereitzustellen, um eine Verbindung als untergeordnetes Fenster herzustellen, wenn der Ereignishandler abgeschlossen ist. Wenn Sie jedoch asynchron erstellen CoreWebView2
müssen, sollten Sie die GetDeferral
-Methode für NewWindowRequestedEventArgs
aufrufen. Nachdem Sie die CoreWebView2
-Eigenschaft NewWindowRequestedEventArgs
asynchron erstellt und festgelegt NewWindow
haben, rufen Sie Complete
für das Deferral
Objekt auf, das von der GetDeferral
-Methode zurückgegeben wird.
Verzögerung in C#
Wenn Sie einen Deferral
in C# verwenden, besteht die bewährte Methode darin, es mit einem using
-Block zu verwenden. Der using
-Block stellt sicher, dass abgeschlossen Deferral
wird, auch wenn eine Ausnahme in der Mitte des using
Blocks ausgelöst wird. Wenn Sie stattdessen Code zum expliziten Aufrufen Complete
von haben, aber vor dem Complete
Aufruf eine Ausnahme ausgelöst wird, wird die Verzögerung erst nach einiger Zeit abgeschlossen, wenn der Garbage Collector die Verzögerung schließlich sammelt und verwirft. In der Zwischenzeit wartet WebView2 darauf, dass der App-Code das Ereignis behandelt.
Führen Sie beispielsweise die folgenden Schritte nicht aus, da das Ereignis nicht als "behandelt" angesehen wird und webView2 diesen Webinhalt nicht rendert, wenn vor dem Aufruf Complete
WebResourceRequested
eine Ausnahme vorliegt.
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();
}
Verwenden Sie stattdessen einen using
-Block, wie im folgenden Beispiel gezeigt. Der using
-Block stellt sicher, dass abgeschlossen Deferral
wird, unabhängig davon, ob eine Ausnahme vorliegt oder nicht.
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);
}
}
Blockieren des UI-Threads
WebView2 basiert auf der Nachrichtenpumpe des UI-Threads, um Ereignishandlerrückrufe und asynchrone Methodenabschlussrückrufe auszuführen. Wenn Sie Methoden verwenden, die die Nachrichtenpumpe blockieren, z Task.Result
. B. oder WaitForSingleObject
, werden Ihre WebView2-Ereignishandler und async-Methoden-Vervollständigungshandler nicht ausgeführt. Der folgende Code wird beispielsweise nicht abgeschlossen, da Task.Result
der Nachrichtenpump beendet wird, während er auf den Abschluss wartet ExecuteScriptAsync
. Da die Nachrichtenpumpe blockiert ist, kann die ExecuteScriptAsync
nicht abgeschlossen werden.
Der folgende Code funktioniert beispielsweise nicht, da er verwendet Task.Result
.
private void Button_Click(object sender, EventArgs e)
{
string result = webView2Control.CoreWebView2.ExecuteScriptAsync("'test'").Result;
MessageBox.Show(this, result, "Script Result");
}
Verwenden Sie stattdessen einen asynchronen await
Mechanismus wie async
und await
, der die Nachrichtenpumpe oder den UI-Thread nicht blockiert. Beispiel:
private async void Button_Click(object sender, EventArgs e)
{
string result = await webView2Control.CoreWebView2.ExecuteScriptAsync("'test'");
MessageBox.Show(this, result, "Script Result");
}
Siehe auch
- Erste Schritte mit WebView2
- WebView2Samples-Repository : ein umfassendes Beispiel für WebView2-Funktionen.
- WebView2-API-Referenz