Access violation during process exit due to ComStaticStorageStatics
Relevant area(s)
WinGet CLI, COM API
Relevant command(s)
No response
Brief description of your issue
It seems WinGet has regressed from 1.10.320 and is once again executing COM code during DLL uninitialization, when COM has already been unloaded.
Steps to reproduce
I tried this code (complete project attached):
int main()
{
winrt::check_hresult(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
const struct Cleanup { ~Cleanup() { CoUninitialize(); } } cleanup;
const auto packageManager{ CreateWinGetInstance<winget::PackageManager>(CLSID_PackageManager) };
auto catalog = packageManager.GetPackageCatalogByName(L"winget");
winrt::check_hresult(catalog.Connect().ExtendedErrorCode());
return 0;
}
WinGetBug-COM-AccessViolation.zip
Expected behavior
It works.
Actual behavior
I get an access violation during process cleanup.
0:000> k
# ChildEBP RetAddr
00 (Inline) -------- WindowsPackageManager!winrt::impl::consume_Windows_ApplicationModel_Core_ICoreApplication<winrt::Windows::ApplicationModel::Core::ICoreApplication>::Properties+0xe [C:\__w\1\b\x86\Release\AppInstallerSharedLib\Generated Files\winrt\Windows.ApplicationModel.Core.h @ 86]
01 (Inline) -------- WindowsPackageManager!winrt::Windows::ApplicationModel::Core::CoreApplication::Properties::__l2::<lambda_1>::operator()+0xe [C:\__w\1\b\x86\Release\AppInstallerSharedLib\Generated Files\winrt\Windows.ApplicationModel.Core.h @ 1082]
02 (Inline) -------- WindowsPackageManager!winrt::impl::call_factory_cast+0x2a [C:\__w\1\b\x86\Release\AppInstallerSharedLib\Generated Files\winrt\base.h @ 6511]
03 (Inline) -------- WindowsPackageManager!winrt::Windows::ApplicationModel::Core::CoreApplication::Properties+0x2a [C:\__w\1\b\x86\Release\AppInstallerSharedLib\Generated Files\winrt\Windows.ApplicationModel.Core.h @ 1082]
04 00b3f590 621f40a3 WindowsPackageManager!AppInstaller::WinRT::COMStaticStorageStatics::ResetAll+0xd7 [C:\__w\1\s\external\pkg\src\AppInstallerSharedLib\COMStaticStorage.cpp @ 34]
05 00b3f5b8 672d29c9 WindowsPackageManager!WindowsPackageManagerInProcModuleTerminate+0x63 [C:\__w\1\s\external\pkg\src\WindowsPackageManager\main.cpp @ 113]
06 00b3f5c0 672d12b7 Microsoft_Management_Deployment_InProc!DllMain+0x29 [C:\__w\1\s\external\pkg\src\Microsoft.Management.Deployment.InProc\dllmain.cpp @ 32]
07 00b3f600 672d139c Microsoft_Management_Deployment_InProc!dllmain_dispatch+0x73 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\dll_dllmain.cpp @ 281]
08 00b3f614 77168ff6 Microsoft_Management_Deployment_InProc!_DllMainCRTStartup+0x1c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\dll_dllmain.cpp @ 334]
09 00b3f634 771a0df3 ntdll!LdrxCallInitRoutine+0x16
0a 00b3f658 7712b11c ntdll!LdrpCallInitRoutineInternal+0x22
0b 00b3f6a0 77127259 ntdll!LdrpCallInitRoutine+0xae
0c 00b3f740 771240fc ntdll!LdrShutdownProcess+0x199
0d 00b3f810 7698b113 ntdll!RtlExitUserProcess+0x17c
WARNING: Stack unwind information not available. Following frames may be wrong.
0e 00b3f824 62caa464 KERNEL32!ExitProcess+0x13
0f 00b3f830 62caa40e ucrtbased!wassert+0x534
10 00b3f868 62caa6d2 ucrtbased!wassert+0x4de
11 00b3f87c 007aa232 ucrtbased!exit+0x12
12 00b3f8dc 007aa0ad WinGetBugTest!__scrt_common_main_seh+0x172 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 295]
13 00b3f8e4 007aa458 WinGetBugTest!__scrt_common_main+0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
14 00b3f8ec 76975d49 WinGetBugTest!mainCRTStartup+0x8 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
15 00b3f8fc 7715d6db KERNEL32!BaseThreadInitThunk+0x19
16 00b3f954 7715d661 ntdll!__RtlUserThreadStart+0x2b
17 00b3f964 00000000 ntdll!_RtlUserThreadStart+0x1b
Environment
WinGet 1.12.350 from NuGet
I'm going to be creating test infrastructure so that we get proper coverage on this and mid-process unload.
I suspect that for this issue we need to not attempt the COM teardown (WindowsPackageManagerInProcModuleTerminate) during DllMain exit. Or better detect the current COM state before doing so.
For the other one, I need to rethink the lifetime mechanics, probably through a more direct invocation rather than reusing the registration system.