// ****************************************************************** // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the MIT License (MIT). // THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH // THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE. // ****************************************************************** using System; using System.Diagnostics; using System.Runtime.InteropServices; using Windows.UI.Notifications; namespace GreenshotWin10Plugin.Native { public static class DesktopNotificationManagerCompat { public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated"; private static bool _registeredAumidAndComServer; private static string _applicationUserModelId; private static bool _registeredActivator; /// /// If not running under the Desktop Bridge, you must call this method to register your applicationUserModelId (AUMID) with the Compat library and to /// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running /// under Desktop Bridge. Call this upon application startup, before calling any other APIs. /// /// An applicationUserModelId (AUMID) that uniquely identifies your application. public static void RegisterAumidAndComServer(string applicationUserModelId) where T : NotificationActivator { if (string.IsNullOrWhiteSpace(applicationUserModelId)) { throw new ArgumentException("You must provide an Application User Model Id (AUMID).", nameof(applicationUserModelId)); } // If running as Desktop Bridge if (DesktopBridgeHelpers.IsRunningAsUwp()) { // Clear the AUMID since Desktop Bridge doesn't use it, and then we're done. // Desktop Bridge apps are registered with platform through their manifest. // Their LocalServer32 key is also registered through their manifest. _applicationUserModelId = null; _registeredAumidAndComServer = true; return; } _applicationUserModelId = applicationUserModelId; string exePath = Process.GetCurrentProcess().MainModule?.FileName; if (exePath != null) { RegisterComServer(exePath); } _registeredAumidAndComServer = true; } /// /// Register the application an a com server /// /// type to register for /// string private static void RegisterComServer(string exePath) where T : NotificationActivator { // We register the EXE to start up when the notification is activated var guid = typeof(T).GUID; if (guid == null) { throw new ArgumentException("You must provide an Guid on your NotificationActivator."); } string regString = $"SOFTWARE\\Classes\\CLSID\\{{{guid}}}\\LocalServer32"; using var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(regString); // Include a flag so we know this was a toast activation and should wait for COM to process // We also wrap EXE path in quotes for extra security key?.SetValue(null, '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG); } /// /// Registers the activator type as a COM server client so that Windows can launch your activator. /// /// Your implementation of NotificationActivator. Must have GUID and ComVisible attributes on class. public static void RegisterActivator() where T : NotificationActivator { // Register type var regService = new RegistrationServices(); regService.RegisterTypeForComClients(typeof(T), RegistrationClassContext.LocalServer, RegistrationConnectionType.MultipleUse); _registeredActivator = true; } /// /// Creates a toast notifier. You must have called first (and also if you're a classic Win32 app), or this will throw an exception. /// /// ToastNotifier public static ToastNotifier CreateToastNotifier() { EnsureRegistered(); if (_applicationUserModelId != null) { // Non-Desktop Bridge return ToastNotificationManager.CreateToastNotifier(_applicationUserModelId); } // Desktop Bridge return ToastNotificationManager.CreateToastNotifier(); } /// /// Gets the object. You must have called first (and also if you're a classic Win32 app), or this will throw an exception. /// public static DesktopNotificationHistoryCompat History { get { EnsureRegistered(); return new DesktopNotificationHistoryCompat(_applicationUserModelId); } } /// /// Checks if the AUMID is correctly registered, if not this throws an exception /// private static void EnsureRegistered() { // If not registered AUMID yet if (!_registeredAumidAndComServer) { // Check if Desktop Bridge if (DesktopBridgeHelpers.IsRunningAsUwp()) { // Implicitly registered, all good! _registeredAumidAndComServer = true; } else { // Otherwise, incorrect usage throw new Exception("You must call RegisterAumidAndComServer first."); } } // If not registered activator yet if (!_registeredActivator) { // Incorrect usage throw new Exception("You must call RegisterActivator first."); } } /// /// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge. /// public static bool CanUseHttpImages => DesktopBridgeHelpers.IsRunningAsUwp(); } }