Modelo de subprocesos para aplicaciones WebView2
Plataformas admitidas: Win32, Windows Forms, WinUI, WPF.
El control WebView2 se basa en el modelo de objetos de componente (COM) y debe ejecutarse en un subproceso de Apartamentos con subproceso único (STA ).
Seguridad para subprocesos
WebView2 debe crearse en un subproceso de interfaz de usuario que use una bomba de mensajes. Todas las devoluciones de llamada se producen en ese subproceso y las solicitudes en WebView2 deben realizarse en ese subproceso. No es seguro usar WebView2 desde otro subproceso.
La única excepción es para la Content
propiedad de CoreWebView2WebResourceRequest
. El Content
flujo de propiedades se lee desde un subproceso en segundo plano. La secuencia debe ser ágil o debe crearse a partir de un STA en segundo plano, para evitar la degradación del rendimiento del subproceso de interfaz de usuario.
Las propiedades del objeto son de un solo subproceso. Por ejemplo, la llamada CoreWebView2CookieManager.GetCookiesAsync(null)
desde un subproceso distinto Main
de se realizará correctamente (es decir, se devuelven cookies); sin embargo, al intentar acceder a las propiedades de las cookies (como c.Domain
) después de dicha llamada se producirá una excepción.
Reentrancy
Las devoluciones de llamada, incluidos los controladores de eventos y los controladores de finalización, se ejecutan en serie. Después de ejecutar un controlador de eventos y comenzar un bucle de mensajes, no se puede ejecutar un controlador de eventos o una devolución de llamada de finalización de forma que vuelva a entrar. Si una aplicación WebView2 intenta crear un bucle de mensajes anidado o una interfaz de usuario modal de forma sincrónica dentro de un controlador de eventos WebView2, este enfoque conduce a un intento de reentrada. Este tipo de reentrancy no se admite en WebView2 y dejaría el controlador de eventos en la pila indefinidamente.
Por ejemplo, no se admite el siguiente enfoque de codificación:
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.
}
}
En su lugar, programe el trabajo adecuado para que tenga lugar después de completar el controlador de eventos, como se muestra en el código siguiente:
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);
}
}
Nota:
Para winforms y aplicaciones WPF, para obtener la pila de llamadas completa con fines de depuración, debe activar la depuración de código nativo para aplicaciones WebView2, como se indica a continuación:
- Abra el proyecto WebView2 en Visual Studio.
- En Explorador de soluciones, haga clic con el botón derecho en el proyecto WebView2 y, a continuación, seleccione Propiedades.
- Seleccione la pestaña Depurar y, a continuación, active la casilla Habilitar depuración de código nativo , como se muestra a continuación.
Aplazamientos
Algunos eventos WebView2 leen valores que se establecen en los argumentos de evento relacionados o inician alguna acción una vez completado el controlador de eventos. Si también necesita ejecutar una operación asincrónica, como un controlador de eventos, use el GetDeferral
método en los argumentos de evento de los eventos asociados. El objeto devuelto Deferral
garantiza que el controlador de eventos no se considere completo hasta que se solicite el Complete
método de Deferral
.
Por ejemplo, puede usar el NewWindowRequested
evento para proporcionar un CoreWebView2
objeto para conectarse como una ventana secundaria cuando se complete el controlador de eventos. Pero si necesita crear de forma asincrónica , CoreWebView2
debe llamar al GetDeferral
método en NewWindowRequestedEventArgs
. Después de crear CoreWebView2
de forma asincrónica y establecer la NewWindow
propiedad en , NewWindowRequestedEventArgs
llame al Complete
Deferral
objeto devuelto por el GetDeferral
método .
Aplazamientos en C#
Cuando se usa en Deferral
C#, el procedimiento recomendado es usarlo con un using
bloque. El using
bloque garantiza que Deferral
se complete incluso si se produce una excepción en medio del using
bloque. Si, en su lugar, tiene código para llamar Complete
explícitamente a , pero se produce una excepción antes Complete
de que se produzca la llamada, el aplazamiento no se completa hasta algún tiempo más tarde, cuando el recolector de elementos no utilizados finalmente recopila y elimina el aplazamiento. Entre tanto, WebView2 espera a que el código de la aplicación controle el evento.
Por ejemplo, no haga lo siguiente, porque si hay una excepción antes de llamar a Complete
, el WebResourceRequested
evento no se considera "controlado" y impide que WebView2 representa ese contenido 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();
}
En su lugar, use un using
bloque, como en el ejemplo siguiente. El using
bloque garantiza que Deferral
se haya completado, independientemente de si hay o no una excepción.
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);
}
}
Bloquear el subproceso de interfaz de usuario
WebView2 se basa en la bomba de mensajes del subproceso de interfaz de usuario para ejecutar devoluciones de llamada del controlador de eventos y devoluciones de llamada de finalización del método asincrónico. Si usa métodos que bloquean la bomba de mensajes, como Task.Result
o WaitForSingleObject
, los controladores de eventos WebView2 y los controladores de finalización de métodos asincrónicos no se ejecutan. Por ejemplo, el código siguiente no se completa, porque Task.Result
detiene la bomba de mensajes mientras espera ExecuteScriptAsync
a que se complete. Dado que la bomba de mensajes está bloqueada, ExecuteScriptAsync
no puede completarse.
Por ejemplo, el código siguiente no funciona, porque usa Task.Result
.
private void Button_Click(object sender, EventArgs e)
{
string result = webView2Control.CoreWebView2.ExecuteScriptAsync("'test'").Result;
MessageBox.Show(this, result, "Script Result");
}
En su lugar, use un mecanismo asincrónico await
como async
y await
, que no bloquea la bomba de mensajes ni el subproceso de interfaz de usuario. Por ejemplo:
private async void Button_Click(object sender, EventArgs e)
{
string result = await webView2Control.CoreWebView2.ExecuteScriptAsync("'test'");
MessageBox.Show(this, result, "Script Result");
}
Vea también
- Introducción a WebView2
- Repositorio WebView2Samples : un ejemplo completo de las funcionalidades de WebView2.
- Referencia de la API de WebView2