Skip to content

Commit abcd507

Browse files
committed
Update C# sample code
1 parent 6ccfc8c commit abcd507

1 file changed

Lines changed: 134 additions & 57 deletions

File tree

specs/ExtendedProcessFailed.md

Lines changed: 134 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)