maui icon indicating copy to clipboard operation
maui copied to clipboard

Winforms process not stopping correctly when using WebView2

Open Mr-Technician opened this issue 3 years ago • 13 comments
trafficstars

.NET version

6.0.5

Did it work in .NET Framework?

Not relevant to WebView2.

Did it work in any of the earlier releases of .NET Core or .NET 5+?

No response

Issue description

I have created a Winforms app using Blazor through WebView2. This works great until I try to close the app.

While running, all of the processes are contained under the parent under the Apps section of task manager: image

After closing the app with the close button, I see this in the task manager under the background process section: image The webview processes also remain in the background: image

If I manually kill the app process at this stage, the WebView processes also stop.

My current fix is the following:

private void Form1_FormClosing(object? sender, FormClosingEventArgs e)
        {
             Environment.Exit(0); //calling Exit kills the background process associated with WebView2
        }

This kills the background process, which in turn kills the WebView2 processes.

Steps to reproduce

Create a new Winforms app and create a blazor webview2 app as per: https://docs.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/windows-forms?view=aspnetcore-6.0

Publish the app and run it, ideally without Visual Studio running to make sure nothing else is running WebView2. Close the app, and check to see if the WebView2 processes are still running.

I have observed this issue on a testing server with the published build, not when running in debug mode from Visual Studio.

Mr-Technician avatar Jun 09 '22 14:06 Mr-Technician

Similar to VS feedback 1546460

Cassie-Li01 avatar Jun 10 '22 02:06 Cassie-Li01

@champnic any thoughts on this?

RussKie avatar Jun 10 '22 07:06 RussKie

I'm not seeing this on a regular Winforms .NET 6 app (without Blazor), nor have I heard other reports of this. I would suspect it's something to do with the app itself or Blazor on handling shutdown, not calling WebView2.Dispose(). Can you verify that Dispose is getting called? Do you see the same issue in a non-Blazor app? + @Eilon in case this is known on the Blazor side.

It's interesting to me that it's not just the WebView2 child processes, but also SRF Certificate Manager child process is also being kept open for some reason.

champnic avatar Jun 10 '22 17:06 champnic

I tried calling dispose on the webview2 object but received this error:

System.NotSupportedException: 'Specified method is not supported.'

when called using

if (disposing)
            {
                blazorWebView1.WebView.Dispose();
            }
            base.Dispose(disposing);

on the main form class.

~~However, the fact that the Certificate Manager process remains in the background suggests calling dispose on WebView2 isn't the underlying issue.~~ I take this back, see below.

I can test using a non blazor app when I am able, hopefully this afternoon.

Mr-Technician avatar Jun 10 '22 18:06 Mr-Technician

Ok, I removed the WebView2 component from the project, commented out all of the services for Blazor, and published + copied to the machine I'm running this on. The app closes and the process does not remain in the background, so it seems that issue is related to WebView.

Mr-Technician avatar Jun 10 '22 18:06 Mr-Technician

Can you try adding a WebView2 without using Blazor?

champnic avatar Jun 10 '22 19:06 champnic

Can you try adding a WebView2 without using Blazor?

I have tested this and the webview2 processes shut down correctly along with the Cert Manager process. So this would confirm it is an issue with WebView2 + Blazor.

Mr-Technician avatar Jun 10 '22 20:06 Mr-Technician

Additionally, I am unable to reproduce this in a blazor webview app in WPF.

Mr-Technician avatar Jun 10 '22 20:06 Mr-Technician

Moved to MAUI repo so we can investigate as a potential Blazor Hybrid issue.

Eilon avatar Jun 13 '22 16:06 Eilon

@MackinnonBuck is this the same disposal related issue that you've fixed recently? This one I think: https://github.com/dotnet/maui/issues/7277 (maybe relevant, not sure)

mkArtakMSFT avatar Jun 29 '22 16:06 mkArtakMSFT

@Eilon shouldn't Blazor WebView be a separate repository? It can be used in MAUI but it is a separate technology - it is strange that BlazorWebView issues from WinForms are solved on MAUI repo. Is it because it's the same team? I've seen mostly Blazor/ASP.NET people working on BlazorWebView since May 2021 when I started following the technology.

janseris avatar Jul 06 '22 21:07 janseris

Hi @janseris , where we placed BlazorWebView was a trade-off and we felt that keeping it in the MAUI repo was the best overall option. The primary scenario was the .NET MAUI BlazorWebView, and also BlazorWebView MAUI+WPF+WinForms depends on nearly everything in .NET, so the MAUI repo fits those requirements. We could place it in a separate repo, but the infrastructure cost to doing that would be enormous, which means that we would have less engineering time to spend on building the features.

Eilon avatar Jul 08 '22 16:07 Eilon

@MackinnonBuck can you please look into this one? Thanks!

mkArtakMSFT avatar Sep 19 '22 17:09 mkArtakMSFT

Hm, I'm not able to repro this behavior. All WebView2-related child process stop when the parent process stops (no matter how I close the Windows Forms app).

@Mr-Technician, could you try using the latest .NET 6 SDK and Microsoft.AspNetCore.Components.WebView.WindowsForms package (I tried with 6.0.540) and see if you're still experiencing the issue?

If the issue persists, would you be able to provide a minimal repro project hosted as a public GitHub repository? There's a chance this issue just doesn't show up when creating a Windows Forms Blazor Hybrid app from the docs. Thanks!

MackinnonBuck avatar Sep 22 '22 18:09 MackinnonBuck

@MackinnonBuck So far the issue remains using the latest .NET 6 SDK. I am preparing a minimal repo now.

Mr-Technician avatar Sep 22 '22 19:09 Mr-Technician

I suspect the core problem is whatever is keeping your app's main process alive, and thus the associated WebView2 processes also remain alive.

Can you see which thread(s) are running in your process? Just run it normally, and then when you get into that state, you can attach the VS debugger to that process. That will let you see which threads are doing what, and could help us determine the root cause.

The only similar issue I could find in the WebView2 repo is this one: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1424. But even that doesn't seem to indicate any known bug in WebView2.

Eilon avatar Sep 22 '22 20:09 Eilon

Here you go: image

For comparison, here is the output from my attempted minimal repo, where i was not able to reproduce the issue: image

The app with the issue uses the Graph API to get and modify applications in Azure.

Mr-Technician avatar Sep 23 '22 15:09 Mr-Technician

@Mr-Technician I'm hoping it's possible for you to try to narrow things down. If you change your app to not make any API calls, does that change anything? Also, can you confirm exactly how the app is being closed? Is the default "X" button in the window's caption bar being clicked, or via some other technique?

Eilon avatar Sep 23 '22 23:09 Eilon

@Eilon No luck so far, I commented out my <App /> component so that nothing loads but the basic structure of the app (zero API calls or anything special) and the process + WebView2 processes linger after closing using the default "X" button.

Mr-Technician avatar Sep 24 '22 01:09 Mr-Technician

I take it back, I think I found the issue:

I commented out my entire <App /> component and the issue was resolved. Here is my minimal App.razor:

<MudThemeProvider />
<MudDialogProvider />
<MudSnackbarProvider />

<MudContainer MaxWidth="MaxWidth.Large">
    @*<Home />*@
</MudContainer>

The Mud components are from MudBlazor: https://github.com/MudBlazor/MudBlazor It would seem that something with one of the Providers listed above causes Blazor to hang.

Mr-Technician avatar Sep 24 '22 01:09 Mr-Technician

@Eilon I have prepared a minimal repo here, complete with simple reproduction steps: https://github.com/Mr-Technician/BlazorWebview2Minimal

Mr-Technician avatar Sep 26 '22 16:09 Mr-Technician

I was able to narrow down the cause for this bug to the following: https://github.com/dotnet/maui/blob/6ee55222aada4476ae320dcee49818bc498dd6d7/src/BlazorWebView/src/WindowsForms/BlazorWebView.cs#L247-L261

There seem to be a couple problems here:

  1. The comment implies that Blazor component disposal logic will complete before base.Dispose() gets executed. This is only true for synchronous component disposal logic, because PageContext.DisposeAsync() calls the synchronous Renderer.Dispose(), which does a fire-and-forget for async disposable components. This means we thought we were waiting for components to dispose asynchronously, but this isn't actually the case (and I've created a repro verifying this, and it can cause nasty exceptions to be thrown). The only thing we're blocking on here is the disposal of the AsyncServiceScope for the web view. This brings us to the actual cause of the bug...
  2. If a service implements IAsyncDisposable and does something actually async (even a Task.Yield()), it will result in a deadlock because we're blocking synchronously on line 255 above.

In this specific case, the MudBlazor MudPopoverService.DisposeAsync() method attempts to perform JS interop when the service disposes, which causes a deadlock for the reason described above.

So I guess my questions are the following:

  1. Why does PageContext call Renderer.Dispose() instead of Renderer.DisposeAsync()? It seems that the issue I described in point 1 above would apply to all Blazor Hybrid platforms. Is this something we should change?
  2. Assuming we keep things the way they are, should we fire-and-forget the call to _webViewManager.DisposeAsync() rather than synchronously blocking? Given that we're already essentially doing that for Blazor component disposal, I don't see much of an advantage to block on disposal of the AsyncServiceScope (especially if it causes a deadlock). We could probably find another way to bubble up any exceptions that get thrown.

cc @Eilon @javiercn

MackinnonBuck avatar Sep 26 '22 21:09 MackinnonBuck

@MackinnonBuck gosh my memory on this is super blurry. Perhaps we can discuss at the next engineering sync?

Eilon avatar Sep 26 '22 23:09 Eilon

Discussed this with @MackinnonBuck. Given the fix would be risky to take at this point, and the fact that there is a workaround available, we're going to address this in 8.0.

mkArtakMSFT avatar Oct 04 '22 21:10 mkArtakMSFT