How to self-register a standalone desktop C# WPF app (distributed as an exe) for local toast notifications?

Viewed 1237

We have a desktop Windows app (written in WFP/C#) that we distribute as a single .exe file with no installer (it bundles all its dependencies via a Fody/Costura plugin).

We would like to integrate a local Action Center toast functionality where the app can display a toast and respond to it when it's clicked.

Displaying the toast is straightforward and can be done by using the Microsoft.Toolkit.Uwp.Notifications nuget package. However, in order to actually receive proper notifications when the toast is clicked in the Action Center (as opposed to the balloon tip) we need to register with notification platform.

The guide on how to do this seems to be focused on apps with an installer (e.g. Wix): https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop?fbclid=IwAR2AoHRKI88VNGRG-pTUytwhkMuovWT4bEr0RoXEayWpWsoGlghtZeq4Mo4#step-4-register-with-notification-platform

The specific task we're trying to achieve is, from the documentation:

If you're using classic Win32 (or if you support both), you have to declare your Application User Model ID (AUMID) and toast activator CLSID (the GUID from step #3) on your app's shortcut in Start.

How can we do it without writing an installer? We would like our app to do this registration on first run.

Note: the app already has provisions for elevating itself through UAC if needed by restarting itself in Administrator context.

Additional references: WPF native windows 10 toasts

[Update]

I managed to follow the instructions in https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop and https://docs.microsoft.com/en-us/windows/win32/shell/enable-desktop-toast-with-appusermodelid to put together what should have been a working solution, but in the end, clicking on toasts in the Action Center does not trigger OnActivated() in my NotificationActivatior.

Salient points:

  1. Sending notification

    var toast = new ToastNotification(toastXml);
    DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
    
  2. Registration:

    string shortcutPath = Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.Programs),
      "Toasty.lnk");
    
    DesktopNotificationManagerCompat.RegisterAumidAndComServer
      <MyNotificationActivator>(AppName);
    
    DesktopNotificationManagerCompat.RegisterActivator
      <MyNotificationActivator>();
    
    if (!File.Exists(shortcutPath))
    {
      ShortcutManager.RegisterAppForNotifications(
        shortcutPath,
        Assembly.GetExecutingAssembly().Location,
        null,
        AppName,
        ActivationId);
     }
    
  3. Creating a shortcut

    public static void RegisterAppForNotifications(
      string shortcutPath,
      string appExecutablePath,
      string arguments,
      string appName,
      string activatorId)
    {
      var shellLinkClass = new ShellLinkCoClass();
      IShellLinkW shellLink = (IShellLinkW)shellLinkClass;
      shellLink.SetPath(appExecutablePath);
    
      IPropertyStore propertyStore = (IPropertyStore)shellLinkClass;
      IPersistFile persistFile = (IPersistFile)shellLinkClass;
    
      if (arguments != null)
      {
        shellLink.SetArguments(arguments);
      }
            
      // https://docs.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-id
      propertyStore.SetValue(
          new PropertyKey("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3", 5),
          new PROPVARIANT(appName));
    
       // https://docs.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-toastactivatorclsid
       propertyStore.SetValue(
         new PropertyKey("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3", 26),
         new PROPVARIANT(new Guid(activatorId)));
       propertyStore.Commit();
    
       persistFile.Save(shortcutPath, true);
    }

[Update]

Finally got it to work - not sure what was wrong before, but the final version seems to be okay. Full code: https://gist.github.com/davidair/c4ea207bf6eece4ef08b97ab29a3036f

1 Answers

I have the same problem with my project now.

Managed to find this repository - https://github.com/felixrieseberg/electron-windows-interactive-notifications

Here's C++ implementation for installing shortcut (InteractiveNotifications file, InstallShortcut method). I guess the problem is how we set the value to PropertyStore, string GUID is not suitable for some reason. Still, I wasn't able to solve the problem for now.

UPDATED: Finally, was able to install shortcut from code! Check my example at Github. https://github.com/romayavorskyi/WpfNotificationTest (still a lot of hardcode, but it should give you the general idea). And you were right, shortcut path matters. It seems shortcut should be in the ProgramData folder for correct work.

Related