Modelo de threading para aplicativos WebView2
Plataformas com suporte: Win32, Windows Forms, WinUI, WPF.
O controle WebView2 é baseado no COM (Component Object Model) e deve ser executado em um thread STA (Apartamentos Threaded Únicos ).
Acesso thread-safe
O WebView2 deve ser criado em um thread de interface do usuário que usa uma bomba de mensagem. Todos os retornos de chamada ocorrem nesse thread e as solicitações no WebView2 devem ser feitas nesse thread. Não é seguro usar o WebView2 de outro thread.
A única exceção é para a Content
propriedade de CoreWebView2WebResourceRequest
. O Content
fluxo de propriedades é lido de um thread em segundo plano. O fluxo deve ser ágil ou deve ser criado a partir de um STA em segundo plano, para evitar a degradação do desempenho do thread da interface do usuário.
As propriedades do objeto são de thread único. Por exemplo, chamar CoreWebView2CookieManager.GetCookiesAsync(null)
de um thread diferente de Main
terá êxito (ou seja, cookies são retornados); no entanto, tentar acessar as propriedades dos cookies (como c.Domain
) após essa chamada gerará uma exceção.
Reentrância
Os retornos de chamada, incluindo manipuladores de eventos e manipuladores de conclusão, são executados serialmente. Depois de executar um manipulador de eventos e iniciar um loop de mensagem, um manipulador de eventos ou um retorno de chamada de conclusão não poderá ser executado de maneira de novo participante. Se um aplicativo WebView2 tentar criar um loop de mensagem aninhado ou uma interface do usuário modal de forma síncrona dentro de um manipulador de eventos WebView2, essa abordagem levará à tentativa de reentrada. Essa reentrada não tem suporte no WebView2 e deixaria o manipulador de eventos na pilha indefinidamente.
Por exemplo, não há suporte para a seguinte abordagem de codificação:
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.
}
}
Em vez disso, agende o trabalho apropriado a ser realizado após a conclusão do manipulador de eventos, conforme mostrado no seguinte código:
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);
}
}
Observação
Para aplicativos WinForms e WPF, para obter a pilha de chamadas completa para fins de depuração, você deve ativar a depuração de código nativo para aplicativos WebView2, da seguinte maneira:
- Abra seu projeto do WebView2 no Visual Studio.
- Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto WebView2 e selecione Propriedades.
- Selecione a guia Depuração e selecione a caixa de seleção Habilitar depuração de código nativo , conforme mostrado abaixo.
Diferimentos
Alguns eventos do WebView2 leem valores definidos nos argumentos de evento relacionados ou iniciam alguma ação após a conclusão do manipulador de eventos. Se você também precisar executar uma operação assíncrona, como um manipulador de eventos, use o GetDeferral
método nos argumentos de evento dos eventos associados. O objeto retornado Deferral
garante que o manipulador de eventos não seja considerado concluído até que o Complete
método do Deferral
seja solicitado.
Por exemplo, você pode usar o NewWindowRequested
evento para fornecer um CoreWebView2
para se conectar como uma janela filho quando o manipulador de eventos for concluído. Mas se você precisar criar assíncronamente o CoreWebView2
, você deve chamar o GetDeferral
método no NewWindowRequestedEventArgs
. Depois de criar assíncronamente o CoreWebView2
e definir a NewWindow
propriedade no NewWindowRequestedEventArgs
, chame Complete
o Deferral
objeto retornado pelo GetDeferral
método.
Adiamentos em C#
Ao usar um Deferral
em C#, a melhor prática é usá-lo com um using
bloco. O using
bloco garante que o Deferral
seja concluído mesmo que uma exceção seja gerada no meio do using
bloco. Se, em vez disso, você tiver código para chamar Complete
explicitamente , mas uma exceção for gerada antes da sua Complete
chamada ocorrer, o adiamento só será concluído algum tempo depois, quando o coletor de lixo eventualmente coletar e descartar o adiamento. Nesse ínterim, o WebView2 aguarda o código do aplicativo para manipular o evento.
Por exemplo, não faça o seguinte, porque se houver uma exceção antes de chamar Complete
, o WebResourceRequested
evento não será considerado "manipulado" e bloqueará o WebView2 de renderizar esse conteúdo da 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();
}
Em vez disso, use um using
bloco, como no exemplo a seguir. O using
bloco garante que o Deferral
seja concluído, se há ou não uma exceção.
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 o thread da interface do usuário
O WebView2 depende da bomba de mensagem do thread da interface do usuário para executar retornos de chamada do manipulador de eventos e retornos de chamada de conclusão do método assíncrono. Se você usar métodos que bloqueiam a bomba de mensagem, como Task.Result
ou WaitForSingleObject
, os manipuladores de eventos Do WebView2 e os manipuladores de conclusão do método assíncrono não serão executados. Por exemplo, o código a seguir não é concluído, pois Task.Result
interrompe a bomba de mensagem enquanto aguarda ExecuteScriptAsync
a conclusão. Como a bomba de mensagem está bloqueada, a ExecuteScriptAsync
não é capaz de ser concluída.
Por exemplo, o código a seguir não funciona, pois ele usa Task.Result
.
private void Button_Click(object sender, EventArgs e)
{
string result = webView2Control.CoreWebView2.ExecuteScriptAsync("'test'").Result;
MessageBox.Show(this, result, "Script Result");
}
Em vez disso, use um mecanismo assíncrono await
como async
e await
, que não bloqueia a bomba de mensagem ou o thread da interface do usuário. Por exemplo:
private async void Button_Click(object sender, EventArgs e)
{
string result = await webView2Control.CoreWebView2.ExecuteScriptAsync("'test'");
MessageBox.Show(this, result, "Script Result");
}
Confira também
- Introdução ao WebView2
- Repositório WebView2Samples – um exemplo abrangente de recursos do WebView2.
- Referência de API do WebView2