Martin
Martin

Reputation: 5623

WinUI notifications: COM exception is raised when instanciating instance of "AppNotification"

I wanted to include notifications in my existing WinUI 3 application which uses Windows App SDK 1.1.4 and .NET 6 (The application does not and shall not use packaging / MSIX).

In order to achieve this, I tried to extract some code of an example application that I created with the "Template studio for WinUI" project template (assistant), see https://github.com/microsoft/TemplateStudio/ (The sample application also works with the 'unpackaged' deployment model).

The code which I extracted from the example application looks like this (the relevant parts should be the methods 'Initialize' and 'Show'):

public class AppNotificationService : IAppNotificationService
{
    public AppNotificationService()
    {

    }

    ~AppNotificationService()
    {
        Unregister();
    }

    public void Initialize()
    {
        AppNotificationManager.Default.NotificationInvoked += OnNotificationInvoked;

        AppNotificationManager.Default.Register();
    }

    public void OnNotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args)
    {
        // TODO: Handle notification invocations when your app is already running.

        //// // Navigate to a specific page based on the notification arguments.
        //// if (ParseArguments(args.Argument)["action"] == "Settings")
        //// {
        ////    App.MainWindow.DispatcherQueue.TryEnqueue(() =>
        ////    {
        ////        _navigationService.NavigateTo(typeof(SettingsViewModel).FullName!);
        ////    });
        //// }

        App.MainWindow.DispatcherQueue.TryEnqueue(() =>
        {
            App.MainWindow.ShowMessageDialogAsync("TODO: Handle notification invocations when your app is already running.", "Notification Invoked");

            App.MainWindow.BringToFront();
        });
    }

    // EXCEPTION IN THIS METHOD
    public bool Show(string payload)
    {
        var appNotification = new AppNotification(payload); // COM EXCEPTION HERE

        AppNotificationManager.Default.Show(appNotification);

        return appNotification.Id != 0;
    }

    public NameValueCollection ParseArguments(string arguments)
    {
        return HttpUtility.ParseQueryString(arguments);
    }

    public void Unregister()
    {
        AppNotificationManager.Default.Unregister();
    }
}

As you can see, the code contains a method "Show" that has a string for the payload that represents the notification message. In addition there is a "Initialize" method that the example code calls upon application startup.

In order to call the "Show" method of the code above, I created some small event handler in my application that gets called when I click a button:

    private void CreateNotification_Click(object sender, RoutedEventArgs e)
    {
        AppNotificationService notificationService = new AppNotificationService();
        notificationService.Initialize();

        string notificationContent = "test";
        notificationService.Show(notificationContent);
    }

However, the call to "notificationService.Show(notificationContent);" always causes a ComException "0xC00CE556" that is raised when the code tries to instanciate the AppNotification instance see here:

enter image description here

I do not know what I am missing here. It seems that the template studio application does something additional to get the notification working, that I am currently not doing in my code. But I have no idea what that is. Any suggestions?

Upvotes: 0

Views: 880

Answers (2)

Andrew KeepCoding
Andrew KeepCoding

Reputation: 13621

I couldn't reproduce your COM Exception but these steps worked.

  1. Create a simple WinUI 3 app project.
  2. Bring AppNotificationService.cs and IAppNotificationService.cs from a TemplateStudio project with app notifications.
  3. Open Package.appxmanifest using a text editor (VSCode).
  4. Add these namespaces:
<Package
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10">

</Package>
  1. Declare these Extensions inside Applications:
<Applications>
  <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
    <uap:VisualElements DisplayName="WinUI3BlankAppProjectTemplate" Description="WinUI3BlankAppProjectTemplate" BackgroundColor="transparent" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png">
      <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
      <uap:SplashScreen Image="Assets\SplashScreen.png" />
    </uap:VisualElements>

    <Extensions>
      <!--Specify which CLSID to activate when notification is clicked-->
      <desktop:Extension Category="windows.toastNotificationActivation">
        <desktop:ToastNotificationActivation ToastActivatorCLSID="12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW" />
      </desktop:Extension>

      <!--Register COM CLSID-->
      <com:Extension Category="windows.comServer">
        <com:ComServer>
          <com:ExeServer Executable="AppNotifications.exe" Arguments="----AppNotificationActivated:" DisplayName="Toast activator">
            <com:Class Id="12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW" DisplayName="Toast activator" />
          </com:ExeServer>
        </com:ComServer>
      </com:Extension>
    </Extensions>

  </Application>
</Applications>
  1. Replace Executable="AppNotifications.exe" with your app name.
  2. Create a GUID from [Tools]-[Create GUID] in VisualStudio menu.
  3. Replace the two GUIDs in the Extensions with the GUID you created.
  4. Save the Package.appxmanifest file and reopen and rebuild the solution.
  5. Call the Show method passing a valid payload. For example:
var xmlPayload =
@"
<toast launch=""action=ToastClick"">
  <visual>
    <binding template=""ToastGeneric"">
      <text>App Notification</text>
      <text></text>
    </binding>
  </visual>
  <actions>
    <action content=""Settings"" arguments=""action=Settings""/>
  </actions>
</toast>
";
appNotificationService.Show(xmlPayload);

UPDATE

For un-packaged(non-packaged) apps you get a COM Exception if you don't call the Initialize() method. So, the step 10. should be something like this:

  1. Call Initialize then Show method passing a valid payload. For example:
AppNotificationService appNotificationService = new();
appNotificationService.Initialize();
var xmlPayload =
@"
<toast launch=""action=ToastClick"">
  <visual>
    <binding template=""ToastGeneric"">
      <text>App Notification</text>
      <text></text>
    </binding>
  </visual>
  <actions>
    <action content=""Settings"" arguments=""action=Settings""/>
  </actions>
</toast>
";
appNotificationService.Show(xmlPayload);

Upvotes: 1

Martin
Martin

Reputation: 5623

I found out what the problem was in my case.

I turned out, that the problem was related to the string I used as notification content. This must not be an arbitrary string (like "test" in the example I used in the question), but an xml string that has a specific format which is needed to represent a "toast" message.

This xml string for the toast message can contain specific elements for text, images and buttons that may appear in the message.

Simple example:

<?xml version="1.0" encoding="utf-8"?>
<toast>
    <visual>
        <binding template="ToastGeneric">
            <text>Some text</text>
        </binding>
    </visual>
</toast>

This article shows an example of the possible syntax of this xml string:

Quickstart: App notifications in the Windows App SDK - 4 Display an app notification


There are also classes and helpers for the construction of the xml string that you can use by installing the "CommunityToolkit.WinUI.Notifications" Nuget package:

ToastContent class

ToastContentBuilder class


Example code

Basically, all you need to do to create a toast notification is this:

private void CreateNotification_Click(object sender, RoutedEventArgs e)
{
    // Version 1: Directly define the xmlPayload string:
    // string xmlPayload = @"<?xml version=""1.0"" encoding=""utf-8""?><toast><visual><binding template=""ToastGeneric""><text>Some text</text></binding></visual></toast>";


    // Version 2: Create the the xmlPayload string using the ToastContentBuilder
    // ToastContentBuilder comes with the "CommunityToolkit.WinUI.Notifications" Nuget package
    ToastContent toastContent = new ToastContentBuilder()
        .AddText("Some text")
        .GetToastContent();

    string xmlPayload = toastContent.GetContent();


    var toast = new AppNotification(xmlPayload);
    AppNotificationManager.Default.Show(toast);
}

In the example code, I have two versions on how to create the xml string that represents the toast message. You can create the xml yourself or use the ToastContentBuilder class from the "CommunityToolkit.WinUI.Notifications" Nuget package.


Personal opinion

I know that StackOverflow does no focus on personal opinions. However, I would like to express that I am quite disappointed to see that a code like

new AppNotification("test");

raises an ComException lacking any useful information instead of an meaningful exception that contains some hint for the developer complaining about the incorrect format of the provided xml string.

Upvotes: 1

Related Questions