Enforce Snackbar usage only in packaged Windows apps - #3276
Conversation
Added runtime checks to ensure Snackbar is enabled and used only in packaged (MSIX) Windows apps, throwing clear exceptions and logging debug messages for unsupported scenarios. Introduced IsPackagedApp() helper method for packaging detection. Updated project file to use $(MauiPackageVersion) for Microsoft.Maui.Controls reference. Improved error handling and developer guidance.
There was a problem hiding this comment.
Pull request overview
This PR fixes a Windows regression where enabling/initializing Snackbar in unpackaged Windows apps can throw WinRT COMExceptions by gating Windows App SDK AppNotificationManager registration behind a “packaged app” check and by providing clearer, actionable exceptions when Snackbar is used without the required Windows setup. It also resolves a Windows PRI resource conflict in the sample by aligning the Microsoft.Maui.Controls package version with the repo-wide MAUI version.
Changes:
- Guard Windows
AppNotificationManager.Register/Unregistercalls so they only run in packaged (MSIX) apps; emit aTrace.WriteLinediagnostic otherwise. - Add a Windows-only
Snackbarconstructor check that throws a clearInvalidOperationExceptionfor unpackaged apps (withHelpLink). - Pin
Microsoft.Maui.Controlsin the sample project to$(MauiPackageVersion)to avoid mixed-MAUI resource conflicts.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/CommunityToolkit.Maui/Options.shared.cs | Gates Windows App SDK notification registration/unregistration behind packaged-app detection and logs diagnostics when unsupported. |
| src/CommunityToolkit.Maui/Alerts/Snackbar/Snackbar.shared.cs | Adds a Windows-only guard to throw a clearer exception when Snackbar is used in an unpackaged app. |
| samples/CommunityToolkit.Maui.Sample/CommunityToolkit.Maui.Sample.csproj | Aligns Microsoft.Maui.Controls package version with the repo-wide MAUI version to prevent PRI build conflicts. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| Alerts.Snackbar.HandleSnackbarAction(args); | ||
| } | ||
|
|
||
| static bool IsPackagedApp() |
There was a problem hiding this comment.
Is it possible to refactor this so that it doesn't require a try/catch block? Using a try/catch block is an expensive operation and if we can avoid it, we should. But if we can't, that's ok too.
There was a problem hiding this comment.
I removed try/catch and replaced it with a reflection call
There was a problem hiding this comment.
I changed it to not use reflection now and it returns true for non null
| internal static TimeSpan GetDefaultTimeSpan() => TimeSpan.FromSeconds(3); | ||
|
|
||
| #if WINDOWS | ||
| static bool IsPackagedApp() |
There was a problem hiding this comment.
Is it possible to refactor this method so that it doesn't require a try/catch block? Using a try/catch block is an expensive operation and if we can avoid it, we should. But if we can't, that's ok too.
There was a problem hiding this comment.
I removed try/catch and replaced it with a reflection call
There was a problem hiding this comment.
I changed it to not use reflection now and it returns true for non null
| <MauiFont Include="Resources\Fonts\*" /> | ||
|
|
||
| <PackageReference Include="Microsoft.Maui.Controls" Version="*" /> | ||
| <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiPackageVersion)" /> |
There was a problem hiding this comment.
Is this necessary?
We keep Microsoft.Maui.Controls version as * to ensure the sample app always uses the most recent MuGet release. This allows us to run + test the sample app on the latest version to avoid breaking changes in our library which runs on an older version of Microsoft.Maui.Controls.
There was a problem hiding this comment.
The wildcard * caused the sample app to resolve Microsoft.Maui.Controls to the latest NuGet version (10.0.90), while every other MAUI package in the repo — including Microsoft.Maui.Controls.Maps, Microsoft.Maui.Core, and the MAUI assemblies pulled in transitively by the CommunityToolkit.Maui.* project references — was pinned to 10.0.60 via $(MauiPackageVersion).
This version mismatch broke the Windows build because both MAUI 10.0.60 and 10.0.90 ship the same framework resource file (Files/Microsoft.Maui/Platform/Windows/Styles/Resources.xbf) but with different content. When the Windows App SDK PRI generator merged resources from both versions, it found the same path with conflicting values, producing:
By pinning the sample to $(MauiPackageVersion) (10.0.60), all MAUI packages resolve to the same version, eliminating the duplicate resource conflict.
Replaced try-catch with reflection in IsPackagedApp() for Windows in Snackbar.shared.cs and Options.shared.cs. The method now checks for Windows.ApplicationModel.Package via reflection and inspects the static Current property, improving robustness and avoiding direct API calls.
|
Thanks James! Could you open up a Docs PR to note this new Snackbar/Toast behavior for unpackaged apps? |
Documentation created! MicrosoftDocs/CommunityToolkit#655 |
| }; | ||
| } | ||
|
|
||
| if (!IsPackagedApp()) |
There was a problem hiding this comment.
Probably should be the first check before configurations
…w message for use in our toolkit
|
I reverted to using try catch again. Otherwise we do not control what the throw message is. |
Description of Change
Fixes a regression introduced in
CommunityToolkit.Mauiv15.0.0 where callingSetShouldEnableSnackbarOnWindows(true)(or otherwise initializing the toolkit's WindowsSnackbar/Toastsupport) in an unpackaged Windows app throws aSystem.Runtime.InteropServices.COMException (0x80670016):or
Root Cause
Options.SetShouldEnableSnackbarOnWindows(true)unconditionally registered the app with the Windows App SDK'sMicrosoft.Windows.AppNotifications.AppNotificationManager, which internally resolves a Windows App Runtime package dependency via the dynamic dependency (Bootstrap) APIs. This registration/resolution only succeeds for packaged (MSIX) applications. When called from an unpackaged app, the underlying WinRT call (IAppNotificationManagerMethods.Register) fails with aCOMException, since the required package dependency and its resource DLL (Microsoft.WindowsAppRuntime.Insights.Resource.dll) cannot be resolved outside of a packaged identity context.Previously (v14.0.1) this registration path was not exercised for unpackaged apps, so the issue was not present until v15.0.0.
Fix
Options.SetShouldEnableSnackbarOnWindowsnow detects whether the app is running as a packaged app (IsPackagedApp(), via reflection overWindows.ApplicationModel.Package.Current) before callingAppNotificationManager.Default.Register()/Unregister(). For unpackaged apps, registration is skipped entirely and aTrace.WriteLinediagnostic message is emitted instead of allowing the unmanagedCOMExceptionto propagate.Snackbar's constructor now throws a clear, actionableInvalidOperationException(instead of letting the unmanaged/WinRTCOMExceptionsurface) when:SetShouldEnableSnackbarOnWindows(true)was not called, orSnackbaron Windows depends on the Windows App SDKAppNotificationManagerAPI, which requires a packaged app identity.HelpLinkpointing to theSnackbardocumentation describing platform-specific initialization requirements.Impact
COMException/missing resource DLL error during startup or when callingSetShouldEnableSnackbarOnWindows(true).InvalidOperationExceptionexplaining thatSnackbarrequires a packaged (MSIX) Windows app, with a link to documentation.AppNotificationManagerregistration continues to work as before.Additional Fix: Windows PRI Build Error (
PRI175/PRI277)While validating the above change, the Windows build of
CommunityToolkit.Maui.Samplefailed with:Root Cause
CommunityToolkit.Maui.Sample.csprojreferencedMicrosoft.Maui.Controlswith a wildcard version:The wildcard resolved to 10.0.90, while every other MAUI package in the repo is pinned to 10.0.60 via
$(MauiPackageVersion)inDirectory.Build.props(includingMicrosoft.Maui.Controls.Mapsand the MAUI assemblies pulled in transitively by theCommunityToolkit.Maui.*project references). The build output confirmed the conflict:Two different MAUI versions each ship the framework resource
Files/Microsoft.Maui/Platform/Windows/Styles/Resources.xbfwith different content. When the Windows App SDK PRI generator merges them, it finds the same resource path with conflicting values, producingPRI175/PRI277.Fix
Pinned
Microsoft.Maui.Controlsto the repo-wide version so all MAUI packages align on 10.0.60:Linked Issues
Fixes:
PR Checklist
approved(bug) orChampioned(feature/proposal)mainat time of PRAdditional information
Unpackaged Sample app in Windows now builds and runs in Release mode.