Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App crashes when exiting an app that uses MenuBar and WebView2 #7260

Closed
1 of 2 tasks
osamu-ikawa opened this issue Jun 20, 2022 · 8 comments
Closed
1 of 2 tasks

App crashes when exiting an app that uses MenuBar and WebView2 #7260

osamu-ikawa opened this issue Jun 20, 2022 · 8 comments
Assignees
Labels
area-WebView bug Something isn't working Crash whenever user reports a crash or app freeze needs-review 👀 product-winui3 WinUI 3 issues team-Rendering Issue for the Rendering team
Milestone

Comments

@osamu-ikawa
Copy link

Describe the bug

In an app that uses MenuBar and WebView2, if you exit the app after performing the following processing, an access violation will occur.

  1. Call WebView2.EnsureCoreWebView2Async.
  2. Add WebView2 to the UI.
  3. Remove WebView2 from the UI after WebView2 is displayed on the screen.

An access violation occurs when the "exit" function is called in "exe_common.inl".

This issue only occurs in Windows App SDK 1.1.X and not in 1.0.3.

Steps to reproduce the bug

This problem is reproduced with a very simple code.

  1. Create a "Blank App, Packaged (WinUI 3 in Desktop)" project in Visual Studio. (Project name: App1)
  2. Update the Windows App SDK to version 1.1.0 or higher with Nuget Package Manager.
  3. Edit code

MainWindow.xaml

<Window
    x:Class="App1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <MenuBar Grid.Row="0">
            <MenuBar.Items>
                <MenuBarItem Title="File">
                    <MenuBarItem.Items>
                        <MenuFlyoutItem Text="Open" Click="OpenButtonClick" />
                    </MenuBarItem.Items>
                </MenuBarItem>
            </MenuBar.Items>
        </MenuBar>

        <Grid x:Name="Holder" Grid.Row="1" />
    </Grid>
</Window>

MainWindow.idl

namespace App1
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window
    {
        MainWindow();
    }
}

MainWindow.xaml.h

#pragma once

#include "MainWindow.g.h"

using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Microsoft::UI::Xaml::Controls;

namespace winrt::App1::implementation
{
    struct MainWindow : MainWindowT<MainWindow>
    {
        MainWindow();

        void OpenButtonClick(IInspectable const& sender, RoutedEventArgs const& e);

        IAsyncAction ShowWebsite(WebView2 web);
    };
}

namespace winrt::App1::factory_implementation
{
    struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
    {
    };
}

MainWindow.xaml.cpp

#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif

#include <winrt/Microsoft.Web.WebView2.Core.h>

using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Microsoft::UI::Xaml::Controls;
using namespace winrt::Microsoft::Web::WebView2::Core;

namespace winrt::App1::implementation
{

    MainWindow::MainWindow()
    {
        InitializeComponent();
    }

    void MainWindow::OpenButtonClick(IInspectable const& sender, RoutedEventArgs const& e) {

        Holder().Children().Clear();

        WebView2 web{};

        ShowWebsite(web);

        Holder().Children().Append(web);

    }

    IAsyncAction MainWindow::ShowWebsite(WebView2 web)
    {
        co_await web.EnsureCoreWebView2Async();

        auto core = web.CoreWebView2();
        core.Navigate(L"https://www.microsoft.com/");

        co_return;
    }

}
  1. Start Debugging (Platform: x64)
  2. Repeat "File"-> "Open" twice from the menu bar.
  3. Close the main window and exit the app.
  4. An access violation occurs. (See screenshot)

Expected behavior

No error occurs when exiting the app.

Screenshots

access_violation

NuGet package version

WinUI 3 - Windows App SDK 1.1.1

Windows app type

  • UWP
  • Win32

Device form factor

Desktop

Windows version

Windows 10 (21H2): Build 19044

Additional context

This issue does not occur if you edit the code as one of the following:

  • Do not remove WebView2 from the UI. (Do not call Holder().Children().Clear().)
  • Do not call EnsureCoreWebView2Async. (Comment out the contents of the ShowWebsite function.)
  • Replace the menu bar with an "Open" button.
    <Button Grid.Row="0" Click="OpenButtonClick">Open</Button>
@osamu-ikawa osamu-ikawa added the bug Something isn't working label Jun 20, 2022
@ghost ghost added the needs-triage Issue needs to be triaged by the area owners label Jun 20, 2022
@krschau
Copy link
Contributor

krschau commented Jun 20, 2022

@osamu-ikawa This is a weird one. Although your instructions were very clear (thanks!), I couldn't repro this on my own. I wouldn't expect any particular interaction between WebView2 and MenuBar that would cause an issue.

Can you tell me what version of the WebView2 Runtime you have on your machine? You can find this in the list of installed apps in settings. If you have a Dev/Canary/Beta channel Edge installed, please let me know.

You could try calling Close() on the WebView2 before you remove it from the UI, that might help. Although it shouldn't be required, it is best practice to Close() webviews when you're done with them.

@krschau krschau added needs-author-feedback Asked author to supply more information. product-winui3 WinUI 3 issues and removed needs-triage Issue needs to be triaged by the area owners labels Jun 20, 2022
@osamu-ikawa
Copy link
Author

osamu-ikawa commented Jun 21, 2022

Edge And WebView2 Runtime version

Edge: 102.0.1245.44
WebView2 Runtime: 102.0.1245.44

I don't have Dev/Canary/Beta channel Edge.

Calling Close() before removing WebView2

I updated the OpenButtonClick function as follows:

    void MainWindow::OpenButtonClick(IInspectable const& sender, RoutedEventArgs const& e) {

        auto children = Holder().Children();

        for (auto child : children)
        {
            child.as<WebView2>().Close();
        }
        children.Clear();

        WebView2 web{};

        ShowWebsite(web);

        children.Append(web);
    }

However, a crash occurred when the app was closed.

Additional Information

I had this problem even if I replaced MenuBar with CommandBar.

        <CommandBar Grid.Row="0" IsOpen="True">
            <AppBarButton Icon="OpenFile" Label="Open" Click="OpenButtonClick" />
        </CommandBar>

After clicking the Open button on the CommandBar twice, a crash occurred when I quit the app.
But strangely, when I changed IsOpen to False and the CommandBar was always closed, the crash no longer occurred.
From the above, I think that the wider UI state is related to the problem rather than the MenuBar.

Sometimes issue is not reproduced, but I don't know the conditions.

@ghost ghost added needs-triage Issue needs to be triaged by the area owners and removed needs-author-feedback Asked author to supply more information. labels Jun 21, 2022
@osamu-ikawa
Copy link
Author

This is the result of analyzing the crash dump by WinDbg. I hope this helps.

Microsoft (R) Windows Debugger Version 10.0.22000.194 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [F:\Users\ikawa\Desktop\App1.dmp]
User Mini Dump File with Full Memory: Only application data is available

Symbol search path is: srv*
Executable search path is: 
Windows 10 Version 19044 MP (8 procs) Free x64
Product: WinNt, suite: SingleUserTS
Edition build lab: 19041.1.amd64fre.vb_release.191206-1406
Machine Name:
Debug session time: Thu Jun 23 18:12:43.000 2022 (UTC + 9:00)
System Uptime: 1 days 8:15:56.471
Process Uptime: 0 days 0:00:40.000
................................................................
.............................................................
Loading unloaded module list
........................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(4d18.2a2c): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
threadpoolwinrt!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+0x58:
00007ffd`293a47f8 488b01          mov     rax,qword ptr [rcx] ds:00000000`00000000=????????????????
0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** WARNING: Unable to verify checksum for App1.exe

KEY_VALUES_STRING: 1

    Key  : AV.Dereference
    Value: NullPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 1249

    Key  : Analysis.DebugAnalysisManager
    Value: Create

    Key  : Analysis.Elapsed.mSec
    Value: 1437

    Key  : Analysis.Init.CPU.mSec
    Value: 3390

    Key  : Analysis.Init.Elapsed.mSec
    Value: 81170

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 159

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 116156

    Key  : Timeline.Process.Start.DeltaSec
    Value: 40

    Key  : WER.OS.Branch
    Value: vb_release

    Key  : WER.OS.Timestamp
    Value: 2019-12-06T14:06:00Z

    Key  : WER.OS.Version
    Value: 10.0.19041.1


NTGLOBALFLAG:  0

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
rax=0000000000000001 rbx=0000000000000001 rcx=0000000000000000
rdx=0000000000000001 rsi=0000000000000001 rdi=0000000000000000
rip=00007ffd293a47f8 rsp=000000f8bb6ff4a0 rbp=00000000ffffffff
 r8=0000000000000002  r9=000000007fffffff r10=00000298d638a050
r11=000000f8bb6ff360 r12=00007ffcb8ed2250 r13=0000000000000000
r14=00007ffcb8c90000 r15=0000000000000001
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=0000  es=0000  fs=0000  gs=0000             efl=00010244
threadpoolwinrt!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+0x58:
00007ffd`293a47f8 488b01          mov     rax,qword ptr [rcx] ds:00000000`00000000=????????????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffd293a47f8 (threadpoolwinrt!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+0x0000000000000058)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000000000000
Attempt to read from address 0000000000000000

PROCESS_NAME:  App1.exe

READ_ADDRESS:  0000000000000000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p          0x%p            Q           B         %s                              B

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000000000000000

STACK_TEXT:  
000000f8`bb6ff4a0 00007ffc`b9001d3f     : 00000000`00000001 00000000`ffffffff 00000000`00000025 00000000`00000001 : threadpoolwinrt!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+0x58
000000f8`bb6ff4d0 00007ffc`b9001b4a     : 00000000`00000025 00000000`00000001 00000000`00000001 00000000`00000001 : Microsoft_UI_Xaml!ThreadPoolService::ReleaseFactories+0x8b
000000f8`bb6ff500 00007ffc`b8edb5c9     : 00000000`00000001 00000000`00000000 00000000`7ffe0385 00000000`00000000 : Microsoft_UI_Xaml!DeinitializeDll+0x62
000000f8`bb6ff540 00007ffc`b8ed21ab     : 00000000`ffffff01 00000000`00000000 00000000`000000d0 00000000`00000000 : Microsoft_UI_Xaml!DllMain+0x39
000000f8`bb6ff570 00007ffd`409e9a1d     : 00007ffc`b8c90000 00000000`00000000 00000000`00000001 00000000`7ffe0385 : Microsoft_UI_Xaml!dllmain_dispatch+0x8f
000000f8`bb6ff5d0 00007ffd`40a2db91     : 00000298`d6244940 00007ffc`b8c90000 00007ffc`00000000 00007ffc`b8ed2e20 : ntdll!LdrpCallInitRoutine+0x61
000000f8`bb6ff640 00007ffd`40a2da2d     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrShutdownProcess+0x141
000000f8`bb6ff740 00007ffd`402ee0ab     : 000000f8`bb6ff790 000000f8`bb6ff798 ffffffff`fffffffe 000000f8`bb6ff794 : ntdll!RtlExitUserProcess+0xad
000000f8`bb6ff770 00007ffc`d8ef1bba     : 000000f8`00000000 000000f8`bb6ff798 ffffffff`fffffffe 00007ff7`72b60108 : kernel32!ExitProcessImplementation+0xb
000000f8`bb6ff7a0 00007ffc`d8ef1b65     : 000000f8`00000000 000000f8`bb6ff808 000000f8`bb6ff840 000000f8`bb6ff7f0 : ucrtbased!exit_or_terminate_process+0x3a
000000f8`bb6ff7d0 00007ffc`d8ef1f06     : 00000298`00000000 00007ff7`00000000 00007ff7`00000000 00007ff7`72b60108 : ucrtbased!common_exit+0x85
000000f8`bb6ff830 00007ff7`72c6e237     : 00007ff7`00000000 00007ff7`72c81330 00000000`00000000 00000000`00000000 : ucrtbased!exit+0x16
000000f8`bb6ff860 00007ff7`72c6e0de     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : App1!__scrt_common_main_seh+0x147
000000f8`bb6ff8d0 00007ff7`72c6e40e     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : App1!__scrt_common_main+0xe
000000f8`bb6ff900 00007ffd`402e7034     : 000000f8`bb4cf000 00000000`00000000 00000000`00000000 00000000`00000000 : App1!wWinMainCRTStartup+0xe
000000f8`bb6ff930 00007ffd`40a22651     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
000000f8`bb6ff960 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21


SYMBOL_NAME:  threadpoolwinrt!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+58

MODULE_NAME: threadpoolwinrt

IMAGE_NAME:  threadpoolwinrt.dll

STACK_COMMAND:  ~0s ; .ecxr ; kb

FAILURE_BUCKET_ID:  NULL_POINTER_READ_c0000005_threadpoolwinrt.dll!Microsoft::WRL::ActivationFactory_Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0_::Release

OS_VERSION:  10.0.19041.1

BUILDLAB_STR:  vb_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

IMAGE_VERSION:  10.0.19041.746

FAILURE_ID_HASH:  {2e40457d-666a-fcfe-b761-6b6e798ba229}

Followup:     MachineOwner
---------

@namazso
Copy link

namazso commented Aug 30, 2022

The reason behind this crash is calling into threadpoolwinrt.dll when it is already freed.

On process shutdown, dlls are unloaded in reverse order. As threadpoolwinrt.dll is loaded later than Microsoft.Ui.Xaml.dll, it gets unloaded first. During unload, it sets Microsoft::WRL::Details::ModuleBase::module_ to nullptr.

Here's a stack trace when this happens:

threadpoolwinrt.dll!Microsoft::WRL::Module<1,Microsoft::WRL::Details::DefaultModule<1> >::~Module<1,Microsoft::WRL::Details::DefaultModule<1> >+0x1e
threadpoolwinrt.dll!Microsoft::WRL::Details::DefaultModule<1>::`vector deleting destructor'+0x14
threadpoolwinrt.dll!Microsoft::WRL::Details::StaticStorage<Microsoft::WRL::Details::DefaultModule<1>,0,int>::~StaticStorage<Microsoft::WRL::Details::DefaultModule<1>,0,int>+0x26
threadpoolwinrt.dll!CRT_INIT+0xc3
threadpoolwinrt.dll!__DllMainCRTStartup+0x1c8
ntdll.dll!LdrpCallInitRoutine+0x61
ntdll.dll!LdrShutdownProcess+0x141
ntdll.dll!RtlExitUserProcess+0xad
kernel32.dll!ExitProcessImplementation+0xb
ucrtbase.dll!exit_or_terminate_process+0x44
ucrtbase.dll!common_exit+0x6f
DuckManager.exe!__scrt_common_main_seh+0x173
kernel32.dll!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

Followed by this, Microsoft.Ui.Xaml.dll unloads, releasing its COM references to Windows.System.Threading.ThreadPoolTimer. Stack trace:

threadpoolwinrt.dll!Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release+0x58
Microsoft.ui.xaml.dll!ThreadPoolService::ReleaseFactories+0x8b
Microsoft.ui.xaml.dll!DeinitializeDll+0x62
Microsoft.ui.xaml.dll!DllMain+0x39
Microsoft.ui.xaml.dll!dllmain_dispatch+0x8f
ntdll.dll!LdrpCallInitRoutine+0x61
ntdll.dll!LdrShutdownProcess+0x141
ntdll.dll!RtlExitUserProcess+0xad
kernel32.dll!ExitProcessImplementation+0xb
ucrtbase.dll!exit_or_terminate_process+0x44
ucrtbase.dll!common_exit+0x6f
DuckManager.exe!__scrt_common_main_seh+0x173
kernel32.dll!BaseThreadInitThunk+0x14
ntdll.dll!RtlUserThreadStart+0x21

However, Microsoft::WRL::ActivationFactory<Windows::System::Threading::IThreadPoolStatics,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,0>::Release tries to call Microsoft::WRL::Details::ModuleBase::module_->DecrementObjectCount(void), but only when the passed object's reference count is 0 or 1 post-dereference. On the path with 0 (1 pre-decrement), there's a nullptr check (even though that shouldn't happen either), however the path with 1 does not have this nullptr check, hence the crash.

This means this crash will occur any time there are two or more references to the ThreadPoolTimer interface, opened from dlls that were loaded earlier and destroyed in their destructors. Xaml keeps one on its own by default, while initializing WebView will cause a second reference, therefore fulfilling the requirements for a crash.

I created a simple C++ program and library to recreate this issue in a cleaner environment:

Dll1.cpp

// compile as dll
#include <windows.h>
#include <roapi.h>
#include <cstdio>
#include <vector>
#include <windows.system.threading.h>

#pragma comment(lib, "windowsapp.lib")

GUID GUID_1a8a9d02_e482_461b_b8c78efad1cce590 = { 0x1a8a9d02, 0xe482, 0x461b, { 0xB8, 0xC7, 0x8E, 0xFA, 0xD1, 0xCC, 0xE5, 0x90 } };
std::vector<IUnknown*> tpts;

constexpr static size_t how_many = 2; // Set to 1 to avoid crash

struct IHaveADestructor
{
  IHaveADestructor() = default;
  ~IHaveADestructor()
  {
    for (const auto tpt : tpts)
      tpt->Release();
  }
};
IHaveADestructor asdasdsa;

__declspec(dllexport) int do_stuff()
{
  RoInitialize(RO_INIT_MULTITHREADED);
  HSTRING hs;
  WindowsCreateString(
    L"Windows.System.Threading.ThreadPoolTimer",
    wcslen(L"Windows.System.Threading.ThreadPoolTimer"),
    &hs);
  for (size_t i = 0; i < how_many; ++i)
  {
    IUnknown* tpt{};
    const auto hr = RoGetActivationFactory(
      hs,
      GUID_1a8a9d02_e482_461b_b8c78efad1cce590,
      (void**)&tpt
    );
    if (!tpt)
    {
      printf("shits broken! %08X\n", hr);
      return -1;
    }
    tpts.push_back(tpt);
  }
  return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    return TRUE;
}

main.cpp

// link against the earlier dll
extern int do_stuff();

int main()
{
  do_stuff();
  return 0;
}

Setting how_many to 1 represents normal Xaml levels of incorrectness, while 2 represents the WebView scenario with two offending modules.

Unfortunately I couldn't come up with a simple workaround, so fixing this is mostly on Microsoft.

The most obvious fix would be not calling ThreadPoolService::ReleaseFactories if deinitialization happens due to process exiting. In general it is not a good practice anyways.

@namazso
Copy link

namazso commented Aug 30, 2022

I posted a workaround in C# / MAUI for this: dotnet/maui#7317 (comment)

@osamu-ikawa
Copy link
Author

@namazso
Thank you for your reply. My app seems to work fine with the workaround you presented. But it looks like Microsoft will fix the problem, so I'll wait for it to be fixed.

@bpulliam bpulliam added the Crash whenever user reports a crash or app freeze label Oct 13, 2022
@Scottj1s Scottj1s self-assigned this Jan 11, 2023
@Scottj1s
Copy link
Member

@namazso, your analysis is spot-on, thank you. The fix is simply to let the global threadpool factory objects "leak" at app shutdown (which of course, the OS will clean up) - as also recommended by Raymond Chen.

@Scottj1s
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-WebView bug Something isn't working Crash whenever user reports a crash or app freeze needs-review 👀 product-winui3 WinUI 3 issues team-Rendering Issue for the Rendering team
Projects
None yet
Development

No branches or pull requests

8 participants