From b8029a2b6fed74c9299b6e492ae5fef8db32c143 Mon Sep 17 00:00:00 2001 From: WebView2 Github Bot Date: Thu, 1 Oct 2026 09:52:09 +0000 Subject: [PATCH 1/2] Updates for Win32, WPF, WinForms, UWP and WinUI3 sample apps from 156.0.4312.0 --- .../WebView2APISample/ProcessComponent.cpp | 10 +- .../WebView2APISample.vcxproj | 4 +- .../ClusterEnvironmentDialog.xaml | 55 ++++++ .../ClusterEnvironmentDialog.xaml.cs | 92 +++++++++ SampleApps/WebView2WpfBrowser/MainWindow.xaml | 8 + .../WebView2WpfBrowser/MainWindow.xaml.cs | 184 +++++++++++++++++- 6 files changed, 343 insertions(+), 10 deletions(-) create mode 100644 SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml create mode 100644 SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml.cs diff --git a/SampleApps/WebView2APISample/ProcessComponent.cpp b/SampleApps/WebView2APISample/ProcessComponent.cpp index c370589d..f8bf1100 100644 --- a/SampleApps/WebView2APISample/ProcessComponent.cpp +++ b/SampleApps/WebView2APISample/ProcessComponent.cpp @@ -125,12 +125,12 @@ ProcessComponent::ProcessComponent(AppWindow* appWindow) //! [CrashReport] // Query for the crash report (available when Crashpad handled // the crash; nullptr for __fastfail / WER-only crashes). - auto experimentalArgs2 = - args.try_query(); - if (experimentalArgs2) + auto processFailedArgs4 = + args.try_query(); + if (processFailedArgs4) { - wil::com_ptr crashReport; - CHECK_FAILURE(experimentalArgs2->get_CrashReport(&crashReport)); + wil::com_ptr crashReport; + CHECK_FAILURE(processFailedArgs4->get_CrashReport(&crashReport)); if (crashReport) { wil::unique_cotaskmem_string crashReportId; diff --git a/SampleApps/WebView2APISample/WebView2APISample.vcxproj b/SampleApps/WebView2APISample/WebView2APISample.vcxproj index edb41b9d..276b911a 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.vcxproj +++ b/SampleApps/WebView2APISample/WebView2APISample.vcxproj @@ -1,4 +1,4 @@ - + @@ -549,4 +549,4 @@ - + \ No newline at end of file diff --git a/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml b/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml new file mode 100644 index 00000000..952f36cc --- /dev/null +++ b/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml.cs b/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml.cs new file mode 100644 index 00000000..787c32f1 --- /dev/null +++ b/SampleApps/WebView2WpfBrowser/ClusterEnvironmentDialog.xaml.cs @@ -0,0 +1,92 @@ +using System.Windows; + +namespace WebView2WpfBrowser +{ + /// + /// Collects the options used to create or join a shared cluster environment. + /// + /// Exposes only plain CLR types rather than + /// CoreWebView2ClusterEnvironmentOptions, because those are experimental + /// APIs absent from the stable package, so naming them here would fail the + /// Stable configuration with CS0246. MainWindow converts inside its + /// USE_WEBVIEW2_EXPERIMENTAL block. + /// + /// The defaults match the Win32 WebView2APISample so both samples pin an + /// identical option set and can join each other's clusters. + /// + public partial class ClusterEnvironmentDialog : Window + { + public ClusterEnvironmentDialog(string defaultClusterName) + { + InitializeComponent(); + + ClusterNameInput.Text = defaultClusterName; + LanguageInput.Text = string.Empty; + BrowserArgsInput.Text = string.Empty; + + SingleSignOnInput.IsChecked = false; + TrackingPreventionInput.IsChecked = true; + BrowserExtensionsInput.IsChecked = false; + ProfileIsolationInput.IsChecked = true; + + StableInput.IsChecked = true; + BetaInput.IsChecked = true; + DevInput.IsChecked = true; + CanaryInput.IsChecked = true; + + // LeastStable so the loader prefers a Canary/Dev channel install over + // the stable Evergreen runtime, which predates the cluster feature. + SearchKindInput.SelectedIndex = 1; + + ClusterNameInput.Focus(); + ClusterNameInput.SelectAll(); + } + + // Trimmed because the cluster name becomes a folder name: the loader + // rejects control characters and a trailing space or dot. + public string ClusterName => ClusterNameInput.Text.Trim(); + + // Named ClusterLanguage rather than Language because FrameworkElement + // already defines a Language property, and the sample builds with + // TreatWarningsAsErrors so CS0108 would fail the build. + public string ClusterLanguage => LanguageInput.Text.Trim(); + + public string AdditionalBrowserArguments => BrowserArgsInput.Text.Trim(); + + public bool AllowSingleSignOnUsingOSPrimaryAccount => SingleSignOnInput.IsChecked == true; + + public bool EnableTrackingPrevention => TrackingPreventionInput.IsChecked == true; + + public bool AreBrowserExtensionsEnabled => BrowserExtensionsInput.IsChecked == true; + + public bool PerHostProfileIsolation => ProfileIsolationInput.IsChecked == true; + + public bool StableChannel => StableInput.IsChecked == true; + + public bool BetaChannel => BetaInput.IsChecked == true; + + public bool DevChannel => DevInput.IsChecked == true; + + public bool CanaryChannel => CanaryInput.IsChecked == true; + + /// + /// 0 = MostStable, 1 = LeastStable. Matches the values of + /// CoreWebView2ChannelSearchKind without naming the experimental enum. + /// + public int ChannelSearchKindIndex => SearchKindInput.SelectedIndex; + + void ok_Clicked(object sender, RoutedEventArgs args) + { + if (string.IsNullOrWhiteSpace(ClusterNameInput.Text)) + { + MessageBox.Show(this, "ClusterName is required.", "Shared Cluster Environment"); + // Put the caret back in the field that needs fixing, rather + // than leaving a keyboard or screen reader user to find it. + ClusterNameInput.Focus(); + ClusterNameInput.SelectAll(); + return; + } + DialogResult = true; + } + } +} diff --git a/SampleApps/WebView2WpfBrowser/MainWindow.xaml b/SampleApps/WebView2WpfBrowser/MainWindow.xaml index 4d2f58ce..56dbd0b6 100644 --- a/SampleApps/WebView2WpfBrowser/MainWindow.xaml +++ b/SampleApps/WebView2WpfBrowser/MainWindow.xaml @@ -52,6 +52,8 @@ found in the LICENSE file. + + @@ -176,6 +178,12 @@ found in the LICENSE file. + + + + + + diff --git a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs index cfe79090..e2b849c7 100644 --- a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs +++ b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs @@ -70,6 +70,8 @@ public partial class MainWindow : Window public static RoutedCommand PinchZoomCommand = new RoutedCommand(); public static RoutedCommand SwipeNavigationCommand = new RoutedCommand(); public static RoutedCommand DeleteProfileCommand = new RoutedCommand(); + public static RoutedCommand ClusterEnvironmentCreateOrJoinCommand = new RoutedCommand(); + public static RoutedCommand ClusterEnvironmentGetOptionsCommand = new RoutedCommand(); public static RoutedCommand SetOriginFeaturesCommand = new RoutedCommand(); public static RoutedCommand GetEffectiveFeaturesForOriginCommand = new RoutedCommand(); public static RoutedCommand NonClientRegionSupportCommand = new RoutedCommand(); @@ -216,6 +218,9 @@ CoreWebView2Profile WebViewProfile private IWebView2 _iWebView2; // Helper reference pointing to the current WV2 control. bool _isNewWindowRequest = false; + // Non-null when this window was opened into an existing (shared cluster) + // environment rather than creating its own. + CoreWebView2Environment _sharedEnvironment = null; List _webViewFrames = new List(); IReadOnlyList _processList = new List(); @@ -262,9 +267,11 @@ public MainWindow() : this(null, false) public MainWindow( CoreWebView2CreationProperties creationProperties = null, - bool isNewWindowRequest = false) + bool isNewWindowRequest = false, + CoreWebView2Environment sharedEnvironment = null) { this.CreationProperties = creationProperties; + _sharedEnvironment = sharedEnvironment; DataContext = this; Loaded += MainWindow_Loaded; Closing += MainWindow_Closing; @@ -272,6 +279,14 @@ public MainWindow( InitializeComponent(); } + // Hosts this window's WebView in an already-created environment instead + // of letting the control create a private one, which is what makes the + // sharing visible. CoreWebView2Environment is stable, so no guard. + public MainWindow(CoreWebView2Environment sharedEnvironment) + : this(null, false, sharedEnvironment) + { + } + private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { // We default to a regular WebView2 control. @@ -332,6 +347,15 @@ async Task InitializeWebView(IWebView2 webView2) // Set background transparent webView2.DefaultBackgroundColor = System.Drawing.Color.Transparent; + // When this window was opened into a shared cluster environment, + // use it as-is. Creating an environment here instead would defeat + // the sharing. + if (_sharedEnvironment != null) + { + await webView2.EnsureCoreWebView2Async(_sharedEnvironment); + return; + } + // Create environment with options and configure WebRTC UDP port range #if USE_WEBVIEW2_EXPERIMENTAL // @@ -1548,6 +1572,158 @@ void DeleteProfileExecuted(object target, ExecutedRoutedEventArgs e) } } + // Default cluster name, shared with the Win32 WebView2APISample so the + // two samples rendezvous on the same cluster out of the box. + const string _defaultClusterName = "SampleCluster"; + + void ClusterEnvironmentCreateOrJoinExecuted(object target, ExecutedRoutedEventArgs e) + { +#if USE_WEBVIEW2_EXPERIMENTAL + // The handler itself stays non-async: the XAML CommandBinding names + // it in every configuration, so it has to exist even where the body + // is compiled out. CreateOrJoinClusterAsync reports its own + // failures, so nothing escapes unobserved. + _ = CreateOrJoinClusterAsync(); +#endif + } + +#if USE_WEBVIEW2_EXPERIMENTAL + async Task CreateOrJoinClusterAsync() + { + var dialog = new ClusterEnvironmentDialog(_defaultClusterName) { Owner = this }; + if (dialog.ShowDialog() != true) + { + return; + } + + // The options object is COM backed through the projection, so its + // setters can fail, which is why they are inside the try. + try + { + CoreWebView2ReleaseChannels channels = 0; + if (dialog.StableChannel) channels |= CoreWebView2ReleaseChannels.Stable; + if (dialog.BetaChannel) channels |= CoreWebView2ReleaseChannels.Beta; + if (dialog.DevChannel) channels |= CoreWebView2ReleaseChannels.Dev; + if (dialog.CanaryChannel) channels |= CoreWebView2ReleaseChannels.Canary; + + var options = new CoreWebView2ClusterEnvironmentOptions + { + ClusterName = dialog.ClusterName, + Language = dialog.ClusterLanguage, + AdditionalBrowserArguments = dialog.AdditionalBrowserArguments, + AllowSingleSignOnUsingOSPrimaryAccount = dialog.AllowSingleSignOnUsingOSPrimaryAccount, + EnableTrackingPrevention = dialog.EnableTrackingPrevention, + AreBrowserExtensionsEnabled = dialog.AreBrowserExtensionsEnabled, + PerHostProfileIsolation = dialog.PerHostProfileIsolation, + ReleaseChannels = channels, + ChannelSearchKind = dialog.ChannelSearchKindIndex == 1 + ? CoreWebView2ChannelSearchKind.LeastStable + : CoreWebView2ChannelSearchKind.MostStable, + }; + + var result = await CoreWebView2Environment.CreateOrJoinClusterEnvironmentAsync(options); + if (result.Status == CoreWebView2ClusterEnvironmentStatus.OptionsMismatch) + { + // A cluster is already running for this ClusterName with a + // different pinned option set, so this host cannot join it. + // Show what it is actually using. + ShowPinnedClusterOptions(dialog.ClusterName); + return; + } + if (result.Status == CoreWebView2ClusterEnvironmentStatus.NotSupported) + { + // Reported as a status rather than an exception, so a real + // application would fall back to a private environment. + MessageBox.Show( + this, + "Cluster environments are not supported in this host process. " + + "Use a private environment instead.", + "Shared Cluster Environment"); + return; + } + if (result.Status != CoreWebView2ClusterEnvironmentStatus.Succeeded) + { + // Only Succeeded carries an environment, so anything the + // enum gains later has none to open a window with. + MessageBox.Show( + this, + "CreateOrJoin reported an unrecognized status.", + "Shared Cluster Environment"); + return; + } + + // Launch the sample again, or another host, with the same + // options to watch several processes share one browser. + new MainWindow(result.Environment).Show(); + } + catch (Exception exception) + { + MessageBox.Show( + this, + "Create or join cluster failed: " + exception.Message, + "Shared Cluster Environment"); + } + } +#endif + + void ClusterEnvironmentGetOptionsExecuted(object target, ExecutedRoutedEventArgs e) + { +#if USE_WEBVIEW2_EXPERIMENTAL + var dialog = new TextInputDialog( + title: "Get Cluster Options", + description: "Enter the cluster name to read its pinned options without spawning a browser.", + defaultInput: _defaultClusterName); + if (dialog.ShowDialog() != true) + { + return; + } + var clusterName = string.IsNullOrWhiteSpace(dialog.Input.Text) + ? _defaultClusterName + : dialog.Input.Text.Trim(); + ShowPinnedClusterOptions(clusterName); +#endif + } + +#if USE_WEBVIEW2_EXPERIMENTAL + // Reads and displays the options pinned for a cluster, or reports that + // no cluster is running for that name. + void ShowPinnedClusterOptions(string clusterName) + { + try + { + var pinned = CoreWebView2Environment.GetClusterEnvironmentOptions(clusterName); + if (pinned == null) + { + MessageBox.Show( + this, + $"No cluster is currently running for ClusterName \"{clusterName}\".", + "Shared Cluster Environment"); + return; + } + + var message = new StringBuilder(); + message.AppendLine($"Options pinned for ClusterName \"{clusterName}\":"); + message.AppendLine(); + message.AppendLine($"Language: {pinned.Language}"); + message.AppendLine($"AdditionalBrowserArguments: {pinned.AdditionalBrowserArguments}"); + message.AppendLine($"AllowSingleSignOnUsingOSPrimaryAccount: {pinned.AllowSingleSignOnUsingOSPrimaryAccount}"); + message.AppendLine($"EnableTrackingPrevention: {pinned.EnableTrackingPrevention}"); + message.AppendLine($"AreBrowserExtensionsEnabled: {pinned.AreBrowserExtensionsEnabled}"); + message.AppendLine($"PerHostProfileIsolation: {pinned.PerHostProfileIsolation}"); + message.AppendLine($"ReleaseChannels: {pinned.ReleaseChannels}"); + message.AppendLine($"ChannelSearchKind: {pinned.ChannelSearchKind}"); + MessageBox.Show(this, message.ToString(), "Shared Cluster Environment"); + } + catch (Exception exception) + { + MessageBox.Show( + this, + "Failed to read the pinned cluster options: " + exception.Message, + "Shared Cluster Environment"); + } + } +#endif + void SetOriginFeaturesCmdExecuted(object target, ExecutedRoutedEventArgs e) { #if USE_WEBVIEW2_EXPERIMENTAL @@ -2427,7 +2603,8 @@ void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2Init { CoreWebView2Deferral deferral = args.GetDeferral(); MainWindow main_window = new MainWindow( - webView.CreationProperties, true /*isNewWindowRequest*/); + webView.CreationProperties, true /*isNewWindowRequest*/, + _sharedEnvironment); main_window.OnWebViewFirstInitialized = () => { using (deferral) @@ -3763,7 +3940,8 @@ void ThrottlingControlExecuted(object target, ExecutedRoutedEventArgs e) { CoreWebView2Deferral deferral = args.GetDeferral(); MainWindow monitorWindow = new MainWindow( - _iWebView2.CreationProperties, true /*isNewWindowRequest*/); + _iWebView2.CreationProperties, true /*isNewWindowRequest*/, + _sharedEnvironment); monitorWindow.OnWebViewFirstInitialized = () => { using (deferral) From 42c9dde7056196eb85853d118511ba411f699c56 Mon Sep 17 00:00:00 2001 From: WebView2 Github Bot Date: Thu, 1 Oct 2026 09:52:34 +0000 Subject: [PATCH 2/2] Updated package version for Win32, WPF and WinForms sample apps to 1.0.4312-prerelease --- SampleApps/WebView2APISample/WebView2APISample.vcxproj | 8 ++++---- SampleApps/WebView2APISample/packages.config | 2 +- .../WebView2WindowsFormsBrowser.csproj | 2 +- SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/SampleApps/WebView2APISample/WebView2APISample.vcxproj b/SampleApps/WebView2APISample/WebView2APISample.vcxproj index 276b911a..41d01a2f 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.vcxproj +++ b/SampleApps/WebView2APISample/WebView2APISample.vcxproj @@ -1,4 +1,4 @@ - + @@ -540,13 +540,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - \ No newline at end of file + diff --git a/SampleApps/WebView2APISample/packages.config b/SampleApps/WebView2APISample/packages.config index e2ace466..e3b576b6 100644 --- a/SampleApps/WebView2APISample/packages.config +++ b/SampleApps/WebView2APISample/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj index d92cc0a1..05e57e21 100644 --- a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj +++ b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj @@ -25,7 +25,7 @@ AnyCPU - + diff --git a/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj b/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj index 1c2c4889..220ef099 100644 --- a/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj +++ b/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj @@ -61,7 +61,7 @@ - +