Child processes leak if the process is killed
I'm submitting a...
- Bug report (I searched for similar issues and did not find one)
Current behavior
Child processes leak if the process is killed. (Win32WebViewHost.exe and WWAHost.exe)
Expected behavior
No child process left running.
Minimal reproduction of the problem with instructions
- Import
Microsoft.Toolkit.Wpf.UI.Controls.WebViewNuGet package. - Run the following snippet:
namespace Repro
{
using Microsoft.Toolkit.Wpf.UI.Controls;
using System;
using System.Diagnostics;
using System.Windows;
class Program
{
[STAThread]
static void Main()
{
var webView = new WebView();
webView.Loaded += (sender, e) =>
{
webView.NavigateToString("webview loaded");
var processId = Process.GetCurrentProcess().Id;
Process.Start("taskkill", $"/F /PID {processId}");
};
var window = new Window
{
Width = 500,
Height = 500,
Content = webView
};
window.Show();
}
}
}
Environment
Nuget Package(s):
- Microsoft.Toolkit.Wpf.UI.Controls.WebView
Package Version(s):
- 5.1.1
Windows 10 Build Number:
- 18362
Device form factor:
- Desktop
I was able to reproduce this bug when testing with package 6.0.0-preview7.1 and W10 version 1903 (build 18362.295). I noticed that the WWAHost process is only left running when the WPF app is terminated with the force option. Thus the command without force...
taskkill /IM TestApp.exe
...correctly triggers WWAHost to exit, whereas if you force termination....
taskkill /IM TestApp.exe /F
...then the WWAHost process is left running.
I think the applicable part of the source code is unavailable so I cannot look at it, but I guess WebViewControlProcess sends a termination message to WWAHost when the app exits, but this termination message cannot be sent when the app is forcibly terminated, thus WWAHost doesn't know that it should exit.
To make a more reliable solution, here's an idea: Maybe WWAHost should use the RegisterWaitForSingleObject function with a handle of the process where the corresponding WebView instance exists. For example, WWAHost could use code similar to this snippet:
void RegisterWaitForProcessExit(DWORD dwProcessID)
{
HANDLE hProcHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);
HANDLE hNewHandle;
RegisterWaitForSingleObject(&hNewHandle, hProcHandle, TestWaitCallback, NULL, INFINITE, WT_EXECUTEONLYONCE);
// TO DO: Use GetLastError to check for errors.
}
VOID CALLBACK TestWaitCallback(_In_ PVOID lpParameter, _In_ BOOLEAN TimerOrWaitFired)
{
...
}
I think the RegisterWaitForSingleObject technique works regardless of whether the monitored/awaited app is politely versus forcibly terminated, but admittedly I might be remembering this fact incorrectly.
This issue might also be partially related to the issue where WebView malfunctions when "explorer.exe" is not running. In the UWP (non-WPF) version of WebView, when you use the option Windows.UI.Xaml.Controls.WebViewExecutionMode.SeparateProcess, when you click the "X" to close/exit the app, the app fails to exit when explorer.exe is not running. I guess Windows.Web.UI.Interop.WebViewControlProcess tries to send a termination message to WWAHost, but this technique fails under either of these 2 circumstances:
- When the app (containing WebView) is forcibly terminated; or
- When "explorer.exe" is not running.
I am facing this issue in my Windows Forms application.
I wanted to note that this issue also appears when the application is killed by an MSI installer update.
Also I am using WebSockets in my WebView application and the WebSocket connections by the leaked processes stay opened which may cause unexpected issues.