Microsoft.Toolkit.Win32 icon indicating copy to clipboard operation
Microsoft.Toolkit.Win32 copied to clipboard

Child processes leak if the process is killed

Open AbeniMatteo opened this issue 6 years ago • 2 comments

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.WebView NuGet 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

AbeniMatteo avatar Jul 18 '19 15:07 AbeniMatteo

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:

  1. When the app (containing WebView) is forcibly terminated; or
  2. When "explorer.exe" is not running.

verelpode avatar Aug 14 '19 12:08 verelpode

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.

ndreisg avatar Mar 25 '20 10:03 ndreisg