@@ -238,9 +238,7 @@ CHECK_FAILURE(m_webView->add_ProcessFailed(
238238
239239 <!-- ... -->
240240
241- <DockPanel
242- x:Name="MyDockPanel"
243- >
241+ <DockPanel>
244242 <!-- ... -->
245243
246244 <DockPanel DockPanel.Dock="Top">
@@ -254,117 +252,196 @@ CHECK_FAILURE(m_webView->add_ProcessFailed(
254252 </TextBox>
255253 </DockPanel>
256254
257- <wv2:WebView2
258- x:Name="webView"
259- CreationProperties="{StaticResource EvergreenWebView2CreationProperties}"
260- Source="https://www.bing.com/"
261- NavigationStarting="WebView_NavigationStarting"
262- NavigationCompleted="WebView_NavigationCompleted"
263- />
255+ <Grid x:Name="Layout">
256+ <wv2:WebView2
257+ x:Name="webView"
258+ CreationProperties="{StaticResource EvergreenWebView2CreationProperties}"
259+ Source="https://www.bing.com/"
260+ />
261+ <!-- The control event handlers are set in code behind so they can be reused when replacing the control after
262+ a WebView2 Runtime's browser process failure
263+ -->
264+ </Grid>
264265 </DockPanel>
265266</Window>
266267
267268```
268269
269270``` c#
270- // This re-instantiates the control and attaches properties as set in the XAML
271- // element. Replace once the control has reinit/uninitialize logic.
272- void ReinitializeWebView ()
271+ public MainWindow ()
273272{
274- webView = new WebView2 ();
273+ InitializeComponent ();
274+ AttachControlEventHandlers (webView );
275+ }
276+
277+ void AttachControlEventHandlers (WebView2 control ) {
278+ control .NavigationStarting += WebView_NavigationStarting ;
279+ control .NavigationCompleted += WebView_NavigationCompleted ;
280+ control .CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted ;
281+ control .KeyDown += WebView_KeyDown ;
282+ }
283+
284+ // The WPF WebView2 control is first added to the visual tree from the webView
285+ // element in the XAML for this class. When we want to replace this instance,
286+ // we need to remove it from the visual tree first.
287+ private bool _isControlInVisualTree = true ;
288+
289+ void RemoveControlFromVisualTree (WebView2 control )
290+ {
291+ Layout .Children .Remove (control );
292+ _isControlInVisualTree = false ;
293+ }
294+
295+ void AttachControlToVisualTree (WebView2 control )
296+ {
297+ Layout .Children .Add (control );
298+ _isControlInVisualTree = true ;
299+ }
275300
276- // Restore URI and other WebView state/setup.
277- webView .CreationProperties = (CoreWebView2CreationProperties )this .FindResource (" EvergreenWebView2CreationProperties" );
278- webView .NavigationStarting += WebView_NavigationStarting ;
279- webView .NavigationCompleted += WebView_NavigationCompleted ;
301+ WebView2 GetReplacementControl ()
302+ {
303+ WebView2 replacementControl = new WebView2 ();
304+ ((System .ComponentModel .ISupportInitialize )(replacementControl )).BeginInit ();
305+ // Setup properties and bindings
306+ replacementControl .CreationProperties = webView .CreationProperties ;
280307
281308 Binding urlBinding = new Binding ()
282309 {
283- Source = webView ,
310+ Source = replacementControl ,
284311 Path = new PropertyPath (" Source" ),
285312 Mode = BindingMode .OneWay
286313 };
287314 url .SetBinding (TextBox .TextProperty , urlBinding );
288315
289- MyDockPanel .Children .Add (webView );
290- webView .Source = _uriToRestore ?? new Uri (" https://www.bing.com" );
316+ AttachControlEventHandlers (replacementControl );
317+ replacementControl .Source = webView .Source ?? new Uri (" https://www.bing.com" );
318+ ((System .ComponentModel .ISupportInitialize )(replacementControl )).EndInit ();
319+
320+ return replacementControl ;
291321}
292322
293- async void WebView_ProcessFailed ( CoreWebView2 sender , CoreWebView2ProcessFailedEventArgs e )
323+ void WebView_CoreWebView2InitializationCompleted ( object sender , CoreWebView2InitializationCompletedEventArgs e )
294324{
295- void AskForReinit ( string message , string caption )
325+ if ( e . IsSuccess )
296326 {
297- // Save URI or other state you want to restore when the WebView is recreated.
298- _uriToRestore = webView .Source ;
299- // Work around. An exception will be thrown while trying to redraw the
300- // control as its CoreWebView2 is in the closed state.
301- webView .Dispose ();
302- webView = null ;
303- var selection = MessageBox .Show (message , caption , MessageBoxButton .YesNo );
304- if (selection == MessageBoxResult .Yes )
327+ webView .CoreWebView2 .ProcessFailed += WebView_ProcessFailed ;
328+ return ;
329+ }
330+
331+ MessageBox .Show ($" WebView2 creation failed with exception = {e .InitializationException }" );
332+ }
333+
334+ void WebView_ProcessFailed (object sender , CoreWebView2ProcessFailedEventArgs e )
335+ {
336+ void ReinitIfSelectedByUser (CoreWebView2ProcessFailedKind kind )
337+ {
338+ string caption ;
339+ string message ;
340+ if (kind == CoreWebView2ProcessFailedKind .BrowserProcessExited )
305341 {
306- // Replace once the control has reinit/uninitialize logic.
307- ReinitializeWebView () ;
342+ caption = " Browser process exited " ;
343+ message = " WebView2 Runtime's browser process exited unexpectedly. Recreate WebView? " ;
308344 }
309345 else
310346 {
311- _uriToRestore = null ;
347+ caption = " Web page unresponsive" ;
348+ message = " WebView2 Runtime's render process stopped responding. Recreate WebView?" ;
349+ }
350+
351+ var selection = MessageBox .Show (message , caption , MessageBoxButton .YesNo );
352+ if (selection == MessageBoxResult .Yes )
353+ {
354+ // The control cannot be re-initialized so we setup a new instance to replace it.
355+ // Note the previous instance of the control has been disposed of and removed from
356+ // the visual tree before attaching the new one.
357+ WebView2 replacementControl = GetReplacementControl ();
358+ if (_isControlInVisualTree )
359+ {
360+ RemoveControlFromVisualTree (webView );
361+ }
362+ // Dispose of the control so additional resources are released. We do this only
363+ // after creating the replacement control as properties for the replacement
364+ // control are taken from the existing instance.
365+ webView .Dispose ();
366+ webView = replacementControl ;
367+ AttachControlToVisualTree (webView );
312368 }
313369 }
314370
315- void AskForReload ( string message , string caption )
371+ void ReloadIfSelectedByUser ( CoreWebView2ProcessFailedKind kind )
316372 {
373+ string caption ;
374+ string message ;
375+ if (kind == CoreWebView2ProcessFailedKind .RenderProcessExited )
376+ {
377+ caption = " Web page unresponsive" ;
378+ message = " WebView2 Runtime's render process exited unexpectedly. Reload page?" ;
379+ }
380+ else
381+ {
382+ caption = " App content frame unresponsive" ;
383+ message = " WebView2 Runtime's render process for app frame exited unexpectedly. Reload page?" ;
384+ }
385+
317386 var selection = MessageBox .Show (message , caption , MessageBoxButton .YesNo );
318387 if (selection == MessageBoxResult .Yes )
319388 {
320389 webView .Reload ();
321390 }
322391 }
323392
324- string message ;
325- string caption ;
393+ bool IsAppContentUri (Uri source )
394+ {
395+ // Sample virtual host name for the app's content.
396+ // See CoreWebView2.SetVirtualHostNameToFolderMapping: https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.setvirtualhostnametofoldermapping
397+ return source .Host == " appassets.example" ;
398+ }
399+
326400 switch (e .ProcessFailedKind )
327401 {
328402 case CoreWebView2ProcessFailedKind .BrowserProcessExited :
329- message = " Browser process exited unexpectedly. Recreate webview?" ;
330- caption = " Browser process exited" ;
331- AskForReinit (message , caption );
332- break ;
403+ // Once the WebView2 Runtime's browser process has crashed,
404+ // the control becomes virtually unusable as the process exit
405+ // moves the CoreWebView2 to its Closed state. Most calls will
406+ // become invalid as they require a backing browser process.
407+ // Remove the control from the visual tree so the framework does
408+ // not atempt to redraw it, which would call the invalid methods.
409+ RemoveControlFromVisualTree (webView );
410+ goto case CoreWebView2ProcessFailedKind .RenderProcessUnresponsive ;
333411 case CoreWebView2ProcessFailedKind .RenderProcessUnresponsive :
334- message = " Browser render process has stopped responding. Recreate webview?" ;
335- caption = " Web page unresponsive" ;
336- AskForReinit (message , caption );
412+ System .Threading .SynchronizationContext .Current .Post ((_ ) =>
413+ {
414+ ReinitIfSelectedByUser (e .ProcessFailedKind );
415+ }, null );
337416 break ;
338417 case CoreWebView2ProcessFailedKind .RenderProcessExited :
339- message = " Browser render process exited unexpectedly. Reload page?" ;
340- caption = " Web page unresponsive" ;
341- AskForReload (message , caption );
418+ System .Threading .SynchronizationContext .Current .Post ((_ ) =>
419+ {
420+ ReloadIfSelectedByUser (e .ProcessFailedKind );
421+ }, null );
342422 break ;
343423 case CoreWebView2ProcessFailedKind .FrameRenderProcessExited :
344424 // A frame-only renderer has exited unexpectedly. Check if reload is needed.
345425 // In this sample we only reload if the app's content has been impacted.
346- foreach (CoreWebView2FrameInfo frameInfo in e .ImpactedFramesInfo )
426+ foreach (CoreWebView2FrameInfo frameInfo in e .FrameInfosForFailedProcess )
347427 {
348- // Sample virtual host name for the app's content.
349- string virtualAppHostName = " https://appassets.example/" ;
350- if (frameInfo .Source .StartsWith (virtualAppHostName ))
428+ if (IsAppContentUri (new System .Uri (frameInfo .Source )))
351429 {
352- message = " Browser render process for app frame exited unexpectedly. Reload page?" ;
353- caption = " App content frame unresponsive" ;
354- AskForReload (message , caption );
355- break ;
430+ goto case CoreWebView2ProcessFailedKind .RenderProcessExited ;
356431 }
357432 }
358433 break ;
359434 default :
360435 // Show the process failure details. Apps can collect info for their logging purposes.
361- caption = " Child process failed" ;
362436 StringBuilder messageBuilder = new StringBuilder ();
363437 messageBuilder .AppendLine ($" Process kind: {e .ProcessFailedKind }" );
364438 messageBuilder .AppendLine ($" Reason: {e .Reason }" );
365439 messageBuilder .AppendLine ($" Exit code: {e .ExitCode }" );
366440 messageBuilder .AppendLine ($" Process description: {e .ProcessDescription }" );
367- MessageBox .Show (messageBuilder .ToString (), caption , MessageBoxButton .OK );
441+ System .Threading .SynchronizationContext .Current .Post ((_ ) =>
442+ {
443+ MessageBox .Show (messageBuilder .ToString (), " Child process failed" , MessageBoxButton .OK );
444+ }, null );
368445 break ;
369446 }
370447}
0 commit comments