Skip to content

Comments

Add scope_exit cleanup to AppNotificationManager::Register#6242

Draft
guimafelipe wants to merge 1 commit intomainfrom
user/felipeda/notifregcleanup
Draft

Add scope_exit cleanup to AppNotificationManager::Register#6242
guimafelipe wants to merge 1 commit intomainfrom
user/felipeda/notifregcleanup

Conversation

@guimafelipe
Copy link
Contributor

Summary

  • Adds wil::scope_exit cleanup guards to both Register() overloads
  • If RegisterComServer fails after RegisterUnpackagedApp succeeds, the full-trust registration is now rolled back via PushNotifications_UnregisterFullTrustApplication
  • Guard is released on success so cleanup only runs on failure

Fixes #2269

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds cleanup logic to the AppNotificationManager::Register methods to handle failures during registration. When RegisterComServer or event handle creation fails after RegisterUnpackagedApp succeeds, the PR ensures that PushNotifications_UnregisterFullTrustApplication is called to roll back the full-trust application registration.

Changes:

  • Adds wil::scope_exit cleanup guards to both Register() overloads
  • Introduces isUnpackaged boolean to replace negated IsPackagedProcess() check for clarity
  • Implements cleanup via PushNotifications_UnregisterFullTrustApplication when subsequent registration steps fail
  • Releases the cleanup guard on successful registration to prevent rollback

if (isUnpackaged)
{
LOG_IF_FAILED(PushNotifications_UnregisterFullTrustApplication(m_appId.c_str()));
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For packaged apps (when !isUnpackaged is true), the cleanup should also handle the toast registration mapping created by RegisterAppNotificationSinkWithLongRunningPlatform.

RegisterPackagedApp (line 237) conditionally calls RegisterAppNotificationSinkWithLongRunningPlatform (line 243) which calls AddToastRegistrationMapping. If RegisterComServer or m_waitHandleForArgs.create() fails after this, the mapping is not cleaned up.

The cleanup lambda should include: if (!PushNotificationHelpers::IsPackagedAppScenario() && !WindowsAppRuntime::SelfContained::IsSelfContained()) { LOG_IF_FAILED(notificationPlatform->RemoveToastRegistrationMapping(...)); }

This matches the cleanup in UnregisterAll() (lines 319-323).

Suggested change
}
}
else if (!PushNotificationHelpers::IsPackagedAppScenario() && !WindowsAppRuntime::SelfContained::IsSelfContained())
{
LOG_IF_FAILED(notificationPlatform->RemoveToastRegistrationMapping(m_appId.c_str()));
}

Copilot uses AI. Check for mistakes.
Comment on lines 111 to 163
void AppNotificationManager::Register()
{
if (!IsSupported())
{
return;
}

auto logTelemetry{ AppNotificationTelemetry::Register::Start(g_telemetryHelper, m_appId) };

{
auto lock{ m_lock.lock_exclusive() };
THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_OPERATION_IN_PROGRESS), m_registering, "Registration is in progress!");
m_registering = true;
}

auto registeringScopeExit{ wil::scope_exit([&]()
{
auto lock { m_lock.lock_exclusive() };
m_registering = false;
}) };

winrt::guid registeredClsid{};
if (AppModel::Identity::IsPackagedProcess())
bool isUnpackaged{ !AppModel::Identity::IsPackagedProcess() };

if (!isUnpackaged)
{
registeredClsid = RegisterPackagedApp();
}
else
{
AppNotificationAssets assets{ GetAssets() };
registeredClsid = RegisterUnpackagedApp(assets);
}

// Cleanup registration on failure of subsequent steps
auto registrationCleanup{ wil::scope_exit([&]()
{
if (isUnpackaged)
{
LOG_IF_FAILED(PushNotifications_UnregisterFullTrustApplication(m_appId.c_str()));
}
}) };

// Create event handle before COM Registration otherwise if a notification arrives will lead to race condition
m_waitHandleForArgs.create();

// Register the AppNotificationManager as a COM server for Shell to Activate and Invoke
RegisterComServer(registeredClsid);

registrationCleanup.release();

logTelemetry.Stop();
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding tests for the registration cleanup logic. Currently, there don't appear to be any tests that verify cleanup happens correctly when Register() fails partway through.

While testing failure scenarios can be challenging, consider adding tests that:

  1. Verify that if registration fails, subsequent re-registration attempts succeed (which would fail if cleanup didn't work)
  2. Verify that registry entries are not left behind after a failed registration
  3. Use mocking or fault injection to simulate failures in RegisterComServer or m_waitHandleForArgs.create()

This would help ensure the cleanup logic works correctly and prevent regressions.

Copilot uses AI. Check for mistakes.
auto registrationCleanup{ wil::scope_exit([&]()
{
if (isUnpackaged)
{
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup is incomplete. If RegisterComServer or m_waitHandleForArgs.create() fails after RegisterUnpackagedApp succeeds, only PushNotifications_UnregisterFullTrustApplication is called. However, RegisterUnpackagedApp also creates other resources that need cleanup:

  1. COM server registry entries via GetOrCreateComActivatorGuid → RegisterComServer (lines 254, 260 in AppNotificationUtility.cpp)
  2. App identifier registry entries via RegisterAssets (line 256)
  3. Toast registration mapping via RegisterAppNotificationSinkWithLongRunningPlatform (line 260)

Compare with UnregisterAll() (lines 333-338) which cleans up all these resources for unpackaged apps:

  • Gets the COM activator GUID and calls UnRegisterComServer
  • Calls UnRegisterNotificationAppIdentifierFromRegistry
  • Calls PushNotifications_UnregisterFullTrustApplication

The cleanup lambda should perform all these cleanup steps, wrapped in LOG_IF_FAILED to handle cases where resources weren't created yet.

Suggested change
{
{
// Best-effort cleanup of all resources created for unpackaged registration.
LOG_IF_FAILED(UnRegisterComServer(registeredClsid));
LOG_IF_FAILED(UnRegisterNotificationAppIdentifierFromRegistry(m_appId.c_str()));

Copilot uses AI. Check for mistakes.

// Cleanup unpackaged registration on failure of subsequent steps
auto registrationCleanup{ wil::scope_exit([&]()
{
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup is incomplete. If RegisterComServer fails after RegisterUnpackagedApp succeeds, only PushNotifications_UnregisterFullTrustApplication is called. However, RegisterUnpackagedApp also creates other resources that need cleanup:

  1. COM server registry entries via GetOrCreateComActivatorGuid → RegisterComServer (lines 254, 260 in AppNotificationUtility.cpp)
  2. App identifier registry entries via RegisterAssets (line 256 in AppNotificationUtility.cpp)
  3. Toast registration mapping via RegisterAppNotificationSinkWithLongRunningPlatform (line 260)

Compare with UnregisterAll() (lines 333-338) which cleans up all these resources for unpackaged apps:

  • Gets the COM activator GUID and calls UnRegisterComServer
  • Calls UnRegisterNotificationAppIdentifierFromRegistry
  • Calls PushNotifications_UnregisterFullTrustApplication

The cleanup lambda should perform all these cleanup steps, wrapped in LOG_IF_FAILED to handle cases where resources weren't created yet.

Suggested change
{
{
// Mirror UnregisterAll() behavior for unpackaged apps: clean up COM server,
// app identifier registry entries, and push notification registration.
LOG_IF_FAILED(UnRegisterComServer(registeredClsid));
LOG_IF_FAILED(UnRegisterNotificationAppIdentifierFromRegistry(m_appId.c_str()));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add cleanup to AppNotificationManager::Register

1 participant