microsoft / microsoft/microsoft-ui-xaml
Memory leaks in MFC App that is using WinUI3 Xaml Island in DLL
- Dominant language
- C++
- Stars
- 8.4k
- Forks
- 942
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 105
Description
### Describe the bug
I have a exe (MFC app) and dll (hosting xaml island code isolated from main app) projects written completly in C++ with help of C++/WinRT and WinAppSDK 1.6. Here is the simplified [repository](https://github.com/PetrMinar/PublicWinUI3Experiment), where the problem can be observed.
When the application exits it reports several memory leaks that originate to the xaml framework. For example:
```
{2840} normal block at 0x0000017C5D4494B0, 32 bytes long.
Data: < [ Z > 20 5B C2 DD FB 7F 00 00 F0 5A C2 DD FB 7F 00 00
{1204} normal block at 0x0000017C596D8060, 80 bytes long.
Data: < U| ` 4]| > A0 C3 E0 55 7C 01 00 00 60 A5 34 5D 7C 01 00 00
{1203} normal block at 0x0000017C5D4C7AB0, 160 bytes long.
Data: 4D 00 69 00 63 00 72 00 6F 00 73 00 6F 00 66 00
{1202} normal block at 0x0000017C59710A00, 16 bytes long.
Data: < mY| > 80 80 6D 59 7C 01 00 00 00 00 00 00 00 00 00 00
{1198} normal block at 0x0000017C596D7880, 80 bytes long.
Data: < U| ` G]| > A0 C3 E0 55 7C 01 00 00 60 EB 47 5D 7C 01 00 00
{1197} normal block at 0x0000017C597037E0, 112 bytes long.
Data: 4D 00 69 00 63 00 72 00 6F 00 73 00 6F 00 66 00
...
```
I was able to identify the last leak {2840} (first in list) - it is a `WeakRefPtr` named `g_previousApplicationWeak` in `FrameworkApplication_Partial.cpp`. When the app exits it calls `FrameworkApplication::GlobalDeinit()` responsible to free it. Comments in the xaml framework code clarifies the situation:
_// Properly releasing the weakptr here requires vccorlib to be loaded, because it's handled
// by vccorlib140[d]!Platform::Details::ControlBlock. But this function is called during DLL
// unload, and vccorlib isn't always around. So for now we just leak the IWeakReference.
// http://task.ms/39377823 XAML can fully unload its own DLL and dependencies after use_
I have found an ugly workaround to prevent this leak (reinitializing `DispatcherQueueController` / `WindowsXamlManager `after framework shutdown). The workaround can be seen in `XamlIslands::Shutdown` in the repository.
As for the other leaks - They are somehow related to loading compiled xaml resources. Callstack of their allocation looks like this:
```
ZXamlslands.dll!std::make_shared() Line 2896
ZXamlslands.dll!winrt::ZXamlIslands::implementation::XamlMetaDataProvider::Provider() Line 54
ZXamlslands.dll!winrt::ZXamlIslands::implementation::XamlMetaDataProvider::GetXamlType(const winrt::hstring & fullName) Line 42
ZXamlslands.dll!winrt::ZXamlIslands::implementation::AppT::GetXamlType(const winrt::hstring & fullName) Line 49
ZXamlslands.dll!winrt::impl::produce::GetXamlTypeByFullName(void * fullName, void * * result) Line 858
Microsoft.ui.xaml.dll!ABI::Microsoft::UI::Xaml::Phone_XamlTypeInfo::XamlLibMetadataProvider::GetXamlTypeByFullName(HSTRING__ * fullName, ABI::Microsoft::UI::Xaml::Markup::IXamlType * * value) Line 126
Microsoft.ui.xaml.dll!DirectUI::MetadataAPI::ImportClassInfoFromMetadataProvider(const xstring_ptr_view & strTypeFullName, const CClassInfo * * ppType) Line 718
Microsoft.ui.xaml.dll!DirectUI::MetadataAPI::TryGetClassInfoByFullName(const xstring_ptr_view & strTypeFullName, bool bSearchCustomTypesOnly, const CClassInfo * * ppType) Line 1228
Microsoft.ui.xaml.dll!DirectUI::MetadataAPI::TryGetClassInfoByName(KnownNamespaceIndex eNamespaceIndex, const xstring_ptr_view & strTypeName, const CClassInfo * * ppType) Line 1270
Microsoft.ui.xaml.dll!XamlManagedTypeInfoProvider::TryGetTypeWithSpecifiedNamespaceId(const KnownNamespaceIndex nNamespaceIndex, const xstring_ptr & spTypeName, XamlTypeToken * ptokType) Line 691
Microsoft.ui.xaml.dll!XamlManagedTypeInfoProvider::ResolveTypeName(const XamlTypeNamespaceToken inNamespaceToken, const xstring_ptr & inTypeName, XamlTypeToken & rXamlType) Line 240
Microsoft.ui.xaml.dll!XamlNativeTypeInfoProvider::ResolveTypeName(const XamlTypeNamespaceToken inNamespaceToken, const xstring_ptr & inTypeName, XamlTypeToken & rXamlType) Line 251
Microsoft.ui.xaml.dll!XamlTypeNamespace::GetXamlType(const xstring_ptr & inTypeName, std::shared_ptr & outType) Line 43
Microsoft.ui.xaml.dll!XamlXmlNamespace::GetXamlTypeImpl(const xstring_ptr & inTypeName, std::shared_ptr & outType) Line 86
Microsoft.ui.xaml.dll!XamlXmlNamespace::GetXamlType(const xstring_ptr & inTypeName, std::shared_ptr & outType) Line 39
Microsoft.ui.xaml.dll!XamlBinaryMetadataReader2::LoadType(unsigned int index) Line 252
Microsoft.ui.xaml.dll!XamlBinaryMetadataReader2::GetType(const unsigned int index, std::shared_ptr & result) Line 600
[Inline Frame] Microsoft.ui.xaml.dll!XamlBinaryMetadataReader2::GetType(const PersistedXamlNode2 &) Line 660
Microsoft.ui.xaml.dll!XamlBinaryFormatSubReader2::ReadXamlType() Line 810
Microsoft.ui.xaml.dll!XamlBinaryFormatSubReader2::ReadCreateTypeBeginInitNode() Line 607
Microsoft.ui.xaml.dll!XamlBinaryFormatSubReader2::TryRead(ObjectWriterNode & currentXamlNode) Line 186
Microsoft.ui.xaml.dll!XamlBinaryFormatSubReader2::TryReadHRESULT(ObjectWriterNode & currentXamlNode, bool * endOfStream) Line 40
Microsoft.ui.xaml.dll!CParser::LoadXamlCore(CCoreServices * pCore, const CParserSettings & parserSettings, const Parser::XamlBuffer & buffer, CDependencyObject * * ppDependencyObject, const xstring_ptr_view & strSourceAssemblyName, const std::array &) Line 269
[Inline Frame] Microsoft.ui.xaml.dll!CParser::LoadXaml(CCoreServices *) Line 34
Microsoft.ui.xaml.dll!CCoreServices::ParseXamlWithExistingFrameworkRoot(const Parser::XamlBuffer & buffer, CDependencyObject * pExistingFrameworkRoot, const xstring_ptr_view & strSourceAssemblyName, const xstring_ptr_view & strXamlResourceUri, const bool expandTemplatesDuringParse, CDependencyObject * * ppDependencyObject) Line 3628
Microsoft.ui.xaml.dll!CApplication::LoadComponent(CCoreServices * pCore, CDependencyObject * pComponent, IPALUri * pUri) Line 493
Microsoft.ui.xaml.dll!CoreImports::Application_LoadComponent(CCoreServices * pCore, CDependencyObject * pComponent, const xstring_ptr & strUri, ComponentResourceLocation resourceLocation) Line 264
Microsoft.ui.xaml.dll!DirectUI::FrameworkApplication::LoadComponent(IInspectable * pComponent, const xstring_ptr & strUri, ABI::Microsoft::UI::Xaml::Controls::Primitives::ComponentResourceLocation resourceLocation) Line 553
Microsoft.ui.xaml.dll!DirectUI::FrameworkApplicationFactory::LoadComponentWithResourceLocationImpl(IInspectable * pComponent, ABI::Windows::Foundation::IUriRuntimeClass * pUri, ABI::Microsoft::UI::Xaml::Controls::Primitives::ComponentResourceLocation resourceLocation) Line 492
Microsoft.ui.xaml.dll!DirectUI::FrameworkApplicationFactory::LoadComponentWithResourceLocation(IInspectable * pComponent, ABI::Windows::Foundation::IUriRuntimeClass * pResourceLocator, ABI::Microsoft::UI::Xaml::Controls::Primitives::ComponentResourceLocation componentResourceLocation) Line 879
[Inline Frame] Microsoft.ui.xaml.dll!DirectUI::FrameworkApplicationFactory::LoadComponentImpl(IInspectable *) Line 475
Microsoft.ui.xaml.dll!DirectUI::FrameworkApplicationFactory::LoadComponent(IInspectable * pComponent, ABI::Windows::Foundation::IUriRuntimeClass * pResourceLocator) Line 861
ZXamlslands.dll!winrt::impl::consume_Microsoft_UI_Xaml_IApplicationStatics::LoadComponent(const winrt::Windows::Foundation::IInspectable & component, const winrt::Windows::Foundation::Uri & resourceLocator) Line 188
ZXamlslands.dll!winrt::Microsoft::UI::Xaml::Application::LoadComponent::__l2::::operator()(const winrt::Microsoft::UI::Xaml::IApplicationStatics & f) Line 12465
ZXamlslands.dll!winrt::impl::factory_cache_entry::call<`winrt::Microsoft::UI::Xaml::Application::LoadComponent'::`2':: &>(winrt::Microsoft::UI::Xaml::Application::LoadComponent::__l2:: & callback) Line 6419
ZXamlslands.dll!winrt::impl::call_factory>(winrt::Microsoft::UI::Xaml::Application::LoadComponent::__l2:: && callback) Line 6441
ZXamlslands.dll!winrt::Microsoft::UI::Xaml::Application::LoadComponent(const winrt::Windows::Foundation::IInspectable & component, const winrt::Windows::Foundation::Uri & resourceLocator) Line 12465
ZXamlslands.dll!winrt::ZXamlIslands::implementation::AppT::InitializeComponent() Line 38
ZXamlslands.dll!winrt::impl::create_and_initialize() Line 7917
ZXamlslands.dll!winrt::make() Line 7998
ZXamlslands.dll!XamlIslands::XxxIslandApp() Line 16
ZXamlslands.dll!XamlIslands::XxxInitializeForCurrentThread::__l2::::operator()() Line 44
ZXamlslands.dll!XamlIslands::XxxInitializeForCurrentThread() Line 35
ZXamlslands.dll!XamlIslands::IslandWndT::Create(std::shared_ptr & Island, HWND__ * Parent, XamlIslands::PageT Page) Line 68
ZXamlslands.dll!XamlIslands::Create(std::shared_ptr & Island, HWND__ * Parent, XamlIslands::PageT Page) Line 171
MFCApp.exe!CMainFrame::OnCreate(tagCREATESTRUCTW * lpCreateStruct) Line 64
mfc140ud.dll!CWnd::OnWndMsg(unsigned int message, unsigned __int64 wParam, __int64 lParam, __int64 * pResult) Line 2409
mfc140ud.dll!CWnd::WindowProc(unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 2100
mfc140ud.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned __int64 wParam, __int64 lParam) Line 265
mfc140ud.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned __int64 wParam, __int64 lParam) Line 418
mfc140ud.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned __int64 wParam, __int64 lParam) Line 299
user32.dll!UserCallWinProcCheckWow()
user32.dll!DispatchClientMessage()
user32.dll!__fnINLPCREATESTRUCT()
ntdll.dll!00007ffca9874114()
win32u.dll!00007ffca6e32294()
user32.dll!VerNtUserCreateWindowEx()
user32.dll!CreateWindowInternal()
user32.dll!CreateWindowExW()
mfc140ud.dll!IsolationAwareCreateWindowExW(unsigned long dwExStyle, const wchar_t * lpClassName, const wchar_t * lpWindowName, unsigned long dwStyle, int X, int Y, int nWidth, int nHeight, HWND__ * hWndParent, HMENU__ * hMenu, HINSTANCE__ * hInstance, void * lpParam) Line 449
mfc140ud.dll!CWnd::CreateEx(unsigned long dwExStyle, const wchar_t * lpszClassName, const wchar_t * lpszWindowName, unsigned long dwStyle, int x, int y, int nWidth, int nHeight, HWND__ * hWndParent, HMENU__ * nIDorHMenu, void * lpParam) Line 727
mfc140ud.dll!CFrameWnd::Create(const wchar_t * lpszClassName, const wchar_t * lpszWindowName, unsigned long dwStyle, const tagRECT & rect, CWnd * pParentWnd, const wchar_t * lpszMenuName, unsigned long dwExStyle, CCreateContext * pContext) Line 622
mfc140ud.dll!CFrameWnd::LoadFrame(unsigned int nIDResource, unsigned long dwDefaultStyle, CWnd * pParentWnd, CCreateContext * pContext) Line 755
MFCApp.exe!CMFCWinUI3App::InitInstance() Line 102
mfc140ud.dll!AfxWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 37
MFCApp.exe!wWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 26
MFCApp.exe!invoke_main() Line 123
MFCApp.exe!__scrt_common_main_seh() Line 288
MFCApp.exe!__scrt_common_main() Line 331
MFCApp.exe!wWinMainCRTStartup(void * __formal) Line 17
kernel32.dll!00007ffca93d257d()
ntdll.dll!00007ffca982af08()
```
### Steps to reproduce the bug
Run the app in provided repository.
### Expected behavior
No leaks after application exit.
Or having a "shutdown function" to do final cleanup before app exit would be enough.
Or mark the allocations as [_IGNORE_BLOCK](https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-debug-heap-details?view=msvc-170) so they are not reported as leak by vc runtime.
### Screenshots
_No response_
### NuGet package version
WinUI 3 - Windows App SDK 1.6.1: 1.6.240923002
### Windows version
Windows 11 (22H2): Build 22621
### Additional context
_No response_
Contributor guide
Research direction
Start by reproducing the shutdown leaks in the simplified PublicWinUI3Experiment repository with WinAppSDK 1.6, then compare the behavior with and without the workaround in XamlIslands::Shutdown. Inspect FrameworkApplication_Partial.cpp and the supplied allocation call stack, especially the g_previousApplicationWeak path and compiled-XAML loading path. Done means identifying whether the leaks are framework-owned and providing a verified resolution or limitation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100