Поделиться через


Потоковая модель для приложений 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 следующим образом:

  1. Откройте проект WebView2 в Visual Studio.
  2. В Обозреватель решений щелкните правой кнопкой мыши проект WebView2 и выберите Пункт Свойства.
  3. Перейдите на вкладку Отладка , а затем установите флажок Включить отладку машинного кода , как показано ниже.

Включение отладки машинного кода в Visual Studio

Рбп

Некоторые события WebView2 считывают значения, заданные для связанных аргументов события, или запускают некоторые действия после завершения обработчика событий. Если вам также нужно выполнить асинхронную операцию, например обработчик событий, используйте GetDeferral метод для аргументов события связанных событий. Возвращаемый Deferral объект гарантирует, что обработчик событий не считается завершенным, пока не Complete будет запрошен метод Deferral объекта .

Например, событие можно использовать для предоставления NewWindowRequestedCoreWebView2 для подключения в качестве дочернего окна по завершении обработчика событий. Но если необходимо асинхронно создать CoreWebView2, следует вызвать GetDeferral метод в NewWindowRequestedEventArgs. После асинхронного создания и задания NewWindow свойства для NewWindowRequestedEventArgs, вызовите CompleteDeferral объект , возвращаемый методом 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");
}

См. также