From d9359b49439c1567803246e4653931d7f61cf8b6 Mon Sep 17 00:00:00 2001 From: James Pike Date: Fri, 29 May 2026 14:15:23 -0700 Subject: [PATCH] AppNotificationManager::Show and AppInstance::GetInstances: refactor to noexcept workers (Bug 61688595) Refactor two hot-path WinRT projection entry points so a failed HRESULT does not trigger the deep C++/WinRT projection rethrow path that exhausted the stack via WIL's FormatMessage-based exception logging chain inside ResultFromCaughtException_CppWinRt::catch$8 (the Watson failure signature for this crash bucket). AppNotificationManager::Show: move the throwing body into a private noexcept HRESULT worker (ShowImpl) using RETURN_HR_IF / RETURN_IF_FAILED / CATCH_RETURN. The public Show keeps the IsSupported() early-return and rethrows any failure HRESULT via THROW_IF_FAILED(ShowImpl(...)). AppLifecycle::AppInstance::GetInstances: same pattern. The body becomes GetInstancesImpl(IVector& out) noexcept; the public GetInstances() is a thin THROW_IF_FAILED(GetInstancesImpl(...)) wrapper. Internal callers (RedirectActivationToAsync at line 636) are unaffected because the public signature is unchanged. These are the two entry points observed in the Watson cabs attached to this bucket: WinUI3 Controls Gallery cabs originate in Show, and the auto-filer cab originates in Photos -> AppInstance::GetInstances (frames 16-22 in the cab call stack). The catch handler one frame deeper is mis-attributed by Watson's PDB walker to SecurityDescriptorHelpers::NonDelegatingGetTrustLevel::catch$0 (lexically-nearest catch symbol). AB#61688595 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dev/AppLifecycle/AppInstance.cpp | 15 +++++++++- dev/AppLifecycle/AppInstance.h | 6 ++++ .../AppNotificationManager.cpp | 29 +++++++++++++------ dev/AppNotifications/AppNotificationManager.h | 6 ++++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/dev/AppLifecycle/AppInstance.cpp b/dev/AppLifecycle/AppInstance.cpp index 72a8441ef5..7fe8b46335 100644 --- a/dev/AppLifecycle/AppInstance.cpp +++ b/dev/AppLifecycle/AppInstance.cpp @@ -285,7 +285,7 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation return s_current.as(); } - IVector AppInstance::GetInstances() + HRESULT AppInstance::GetInstancesImpl(IVector& instancesOut) noexcept try { // Force the singleton init. GetCurrent(); @@ -332,6 +332,19 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation } } + instancesOut = instances; + return S_OK; + } + CATCH_RETURN() + + IVector AppInstance::GetInstances() + { + // Delegate to the noexcept GetInstancesImpl worker and rethrow any failure + // HRESULT cleanly via wil::ResultException. This avoids the deep WIL + // FormatMessage-based exception logging chain that caused stack overflow on + // the hot path (Bug 61688595). + IVector instances{ nullptr }; + THROW_IF_FAILED(GetInstancesImpl(instances)); return instances; } diff --git a/dev/AppLifecycle/AppInstance.h b/dev/AppLifecycle/AppInstance.h index f1c7771a8a..9757b486ca 100644 --- a/dev/AppLifecycle/AppInstance.h +++ b/dev/AppLifecycle/AppInstance.h @@ -54,6 +54,12 @@ namespace winrt::Microsoft::Windows::AppLifecycle::implementation void EnqueueRedirectionRequestId(GUID id); GUID DequeueRedirectionRequestId(); + // noexcept HRESULT-returning worker for GetInstances. The public GetInstances + // boundary is intentionally thin so a failed HRESULT does not trigger the deep + // C++/WinRT projection rethrow path that exhausted the stack via WIL's + // FormatMessage-based exception logging (Bug 61688595). + static HRESULT GetInstancesImpl(winrt::Windows::Foundation::Collections::IVector& instancesOut) noexcept; + // Named object prefixes used to scope. std::wstring m_moduleName; std::wstring m_processName; diff --git a/dev/AppNotifications/AppNotificationManager.cpp b/dev/AppNotifications/AppNotificationManager.cpp index bcee2ddfc5..71f0a3ea32 100644 --- a/dev/AppNotifications/AppNotificationManager.cpp +++ b/dev/AppNotifications/AppNotificationManager.cpp @@ -421,13 +421,8 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation } CATCH_RETURN() - void AppNotificationManager::Show(winrt::Microsoft::Windows::AppNotifications::AppNotification const& notification) + HRESULT AppNotificationManager::ShowImpl(winrt::Microsoft::Windows::AppNotifications::AppNotification const& notification) noexcept try { - if (!IsSupported()) - { - return; - } - auto logTelemetry{ AppNotificationTelemetry::Show::Start( g_telemetryHelper, m_appId, @@ -436,21 +431,37 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation notification.Group(), winrt::AppNotificationConferencingConfig::IsCallingPreviewSupported()) }; - THROW_HR_IF(WPN_E_NOTIFICATION_POSTED, notification.Id() != 0); + RETURN_HR_IF(WPN_E_NOTIFICATION_POSTED, notification.Id() != 0); winrt::com_ptr<::ABI::Microsoft::Internal::ToastNotifications::INotificationProperties> notificationProperties = winrt::make_self(notification); winrt::com_ptr<::ABI::Microsoft::Internal::ToastNotifications::INotificationTransientProperties> notificationTransientProperties = winrt::make_self(notification); DWORD notificationId = 0; - THROW_IF_FAILED(ToastNotifications_PostToast(m_appId.c_str(), notificationProperties.get(), notificationTransientProperties.get(), ¬ificationId)); + RETURN_IF_FAILED(ToastNotifications_PostToast(m_appId.c_str(), notificationProperties.get(), notificationTransientProperties.get(), ¬ificationId)); - THROW_HR_IF(E_UNEXPECTED, notificationId == 0); + RETURN_HR_IF(E_UNEXPECTED, notificationId == 0); implementation::AppNotification* notificationImpl = get_self(notification); notificationImpl->SetNotificationId(notificationId); logTelemetry.Stop(); + return S_OK; + } + CATCH_RETURN() + + void AppNotificationManager::Show(winrt::Microsoft::Windows::AppNotifications::AppNotification const& notification) + { + if (!IsSupported()) + { + return; + } + + // Delegate to the noexcept ShowImpl worker and rethrow any failure HRESULT + // cleanly via wil::ResultException. This avoids the deep WIL + // FormatMessage-based exception logging chain that caused stack overflow on + // the hot path (Bug 61688595). + THROW_IF_FAILED(ShowImpl(notification)); } winrt::Windows::Foundation::IAsyncOperation AppNotificationManager::UpdateAsync(winrt::Microsoft::Windows::AppNotifications::AppNotificationProgressData const data, hstring const tag, hstring const group) diff --git a/dev/AppNotifications/AppNotificationManager.h b/dev/AppNotifications/AppNotificationManager.h index 7282fc0e59..09dfa8cfff 100644 --- a/dev/AppNotifications/AppNotificationManager.h +++ b/dev/AppNotifications/AppNotificationManager.h @@ -58,6 +58,12 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation void UnregisterHelper(); + // noexcept HRESULT-returning worker for Show. The public Show boundary is + // intentionally thin so a failed HRESULT does not trigger the deep C++/WinRT + // projection rethrow path that exhausted the stack via WIL's FormatMessage-based + // exception logging (Bug 61688595). + HRESULT ShowImpl(winrt::Microsoft::Windows::AppNotifications::AppNotification const& notification) noexcept; + wil::unique_com_class_object_cookie m_notificationComActivatorRegistration; wil::srwlock m_lock; winrt::event m_notificationHandlers;