Потоковая модель для приложений WebView2
Поддерживаемые платформы: Win32, Windows Forms, WinUI, WPF.
Элемент управления WebView2 основан на объектной модели компонентов (COM) и должен выполняться в потоке с одним потоком (STA).
Потокобезопасность
WebView2 должен быть создан в потоке пользовательского интерфейса, который использует насос сообщений. Все обратные вызовы выполняются в этом потоке, и запросы в WebView2 должны выполняться в этом потоке. Небезопасно использовать WebView2 из другого потока.
Единственное исключение — для Content
свойства CoreWebView2WebResourceRequest
. Поток Content
свойств считывается из фонового потока. Поток должен быть гибким или создаваться из фонового STA, чтобы предотвратить снижение производительности потока пользовательского интерфейса.
Свойства объекта являются однопоточными. Например, вызов CoreWebView2CookieManager.GetCookiesAsync(null)
из потока, отличного Main
от , будет выполнен успешно (то есть возвращаются файлы cookie). Однако попытка получить доступ к свойствам файлов cookie (например c.Domain
, ) после такого вызова вызовет исключение.
Повторный вход
Обратные вызовы, включая обработчики событий и обработчики завершения, выполняются последовательно. После запуска обработчика событий и запуска цикла сообщений обработчик событий или обратный вызов завершения невозможно выполнить повторно. Если приложение WebView2 пытается создать вложенный цикл сообщений или модальный пользовательский интерфейс синхронно в обработчике событий WebView2, такой подход приводит к попытке повторного входа. Такая повторность не поддерживается в 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 следующим образом:
- Откройте проект WebView2 в Visual Studio.
- В Обозреватель решений щелкните правой кнопкой мыши проект WebView2 и выберите Пункт Свойства.
- Перейдите на вкладку Отладка , а затем установите флажок Включить отладку машинного кода , как показано ниже.
Рбп
Некоторые события WebView2 считывают значения, заданные для связанных аргументов события, или запускают некоторые действия после завершения обработчика событий. Если вам также нужно выполнить асинхронную операцию, например обработчик событий, используйте GetDeferral
метод для аргументов события связанных событий. Возвращаемый Deferral
объект гарантирует, что обработчик событий не считается завершенным, пока не Complete
будет запрошен метод Deferral
объекта .
Например, событие можно использовать для предоставления NewWindowRequested
CoreWebView2
для подключения в качестве дочернего окна по завершении обработчика событий. Но если необходимо асинхронно создать CoreWebView2
, следует вызвать GetDeferral
метод в NewWindowRequestedEventArgs
. После асинхронного создания и задания NewWindow
свойства для NewWindowRequestedEventArgs
, вызовите Complete
Deferral
объект , возвращаемый методом GetDeferral
.CoreWebView2
Отсрочки в C#
При использовании Deferral
в C# рекомендуется использовать его с блоком using
. Блок using
гарантирует завершение Deferral
, даже если в середине using
блока создается исключение. Если вместо этого у вас есть код для явного вызова Complete
, но перед вызовом Complete
возникает исключение, то отсрочка завершается только через некоторое время, когда сборщик мусора в конечном итоге собирает и удаляет отсрочку. В то же время WebView2 ожидает, пока код приложения обработает событие.
Например, не делайте следующее, так как если перед вызовом Complete
возникло исключение, WebResourceRequested
событие не считается обработанным и не позволяет WebView2 отрисовки этого веб-содержимого.
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);
}
}
Блокировка потока пользовательского интерфейса
WebView2 использует насос сообщений потока пользовательского интерфейса для выполнения обратных вызовов обработчика событий и обратных вызовов завершения асинхронного метода. Если вы используете методы, которые блокируют насос сообщений, например 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
, который не блокирует насос сообщений или поток пользовательского интерфейса. Пример:
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.
- Справочник по API WebView2