diff --git a/DFAssist.Core/DFAssist.Core.csproj b/DFAssist.Core/DFAssist.Core.csproj index a2ea2b0..97d9129 100644 --- a/DFAssist.Core/DFAssist.Core.csproj +++ b/DFAssist.Core/DFAssist.Core.csproj @@ -10,6 +10,7 @@ DFAssist.Core DFAssist.Core v4.7.2 + 10.0.10240.0 512 true @@ -57,9 +58,12 @@ + + + @@ -77,10 +81,17 @@ - Properties\SharedAssemblyInfo.cs + + + + + + + + diff --git a/DFAssist.Core/Toast/Base/DesktopBridgeHelpers.cs b/DFAssist.Core/Toast/Base/DesktopBridgeHelpers.cs new file mode 100644 index 0000000..b4848a8 --- /dev/null +++ b/DFAssist.Core/Toast/Base/DesktopBridgeHelpers.cs @@ -0,0 +1,70 @@ +// ****************************************************************** +// 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.Runtime.InteropServices; +using System.Text; + +namespace DFAssist.Core.Toast.Base +{ + /// + /// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs + /// + public static class DesktopBridgeHelpers + { + // ReSharper disable InconsistentNaming + // ReSharper disable ArrangeTypeMemberModifiers + const long APPMODEL_ERROR_NO_PACKAGE = 15700L; + // ReSharper restore ArrangeTypeMemberModifiers + // ReSharper restore InconsistentNaming + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName); + + private static bool? _isRunningAsUwp; + public static bool IsRunningAsUwp() + { + if (_isRunningAsUwp != null) + return _isRunningAsUwp.Value; + + if (IsWindows7OrLower) + { + _isRunningAsUwp = false; + } + else + { + var length = 0; + var sb = new StringBuilder(0); + // ReSharper disable RedundantAssignment + var result = GetCurrentPackageFullName(ref length, sb); + // ReSharper restore RedundantAssignment + sb = new StringBuilder(length); + result = GetCurrentPackageFullName(ref length, sb); + + _isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE; + } + + return _isRunningAsUwp.Value; + } + + private static bool IsWindows7OrLower + { + get + { + var versionMajor = Environment.OSVersion.Version.Major; + var versionMinor = Environment.OSVersion.Version.Minor; + var version = versionMajor + (double)versionMinor / 10; + return version <= 6.1; + } + } + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/Base/DesktopNotificationHistoryCompat.cs b/DFAssist.Core/Toast/Base/DesktopNotificationHistoryCompat.cs new file mode 100644 index 0000000..341995c --- /dev/null +++ b/DFAssist.Core/Toast/Base/DesktopNotificationHistoryCompat.cs @@ -0,0 +1,98 @@ +// ****************************************************************** +// 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 Windows.UI.Notifications; + +namespace DFAssist.Core.Toast.Base +{ + /// + /// Manages the toast notifications for an app including the ability the clear all toast history and removing individual toasts. + /// + public sealed class DesktopNotificationHistoryCompat + { + private readonly string _aumid; + private readonly ToastNotificationHistory _history; + + /// + /// Do not call this. Instead, call to obtain an instance. + /// + internal DesktopNotificationHistoryCompat(string aumid) + { + _aumid = aumid; + _history = ToastNotificationManager.History; + } + + /// + /// Removes all notifications sent by this app from action center. + /// + public void Clear() + { + if (_aumid != null) + { + _history.Clear(_aumid); + } + else + { + _history.Clear(); + } + } + + /// + /// Removes an individual toast, with the specified tag label, from action center. + /// + /// The tag label of the toast notification to be removed. + public void Remove(string tag) + { + if (_aumid != null) + { + _history.Remove(tag, string.Empty, _aumid); + } + else + { + _history.Remove(tag); + } + } + + /// + /// Removes a toast notification from the action using the notification's tag and group labels. + /// + /// The tag label of the toast notification to be removed. + /// The group label of the toast notification to be removed. + public void Remove(string tag, string group) + { + if (_aumid != null) + { + _history.Remove(tag, group, _aumid); + } + else + { + _history.Remove(tag, group); + } + } + + /// + /// Removes a group of toast notifications, identified by the specified group label, from action center. + /// + /// The group label of the toast notifications to be removed. + public void RemoveGroup(string group) + { + if (_aumid != null) + { + _history.RemoveGroup(group, _aumid); + } + else + { + _history.RemoveGroup(group); + } + } + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/Base/DesktopNotificationManagerCompat.cs b/DFAssist.Core/Toast/Base/DesktopNotificationManagerCompat.cs new file mode 100644 index 0000000..4faa368 --- /dev/null +++ b/DFAssist.Core/Toast/Base/DesktopNotificationManagerCompat.cs @@ -0,0 +1,153 @@ +// ****************************************************************** +// 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 DFAssist.Core.Toast.Base +{ + public class DesktopNotificationManagerCompat + { + // ReSharper disable InconsistentNaming + public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated"; + // ReSharper restore InconsistentNaming + + private static bool _registeredAumidAndComServer; + private static string _aumid; + private static bool _registeredActivator; + + /// + /// If not running under the Desktop Bridge, you must call this method to register your 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 AUMID that uniquely identifies your application. + public static void RegisterAumidAndComServer(string aumid) + where T : NotificationActivator + { + if (string.IsNullOrWhiteSpace(aumid)) + { + throw new ArgumentException("You must provide an AUMID.", nameof(aumid)); + } + + // 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. + _aumid = null; + _registeredAumidAndComServer = true; + return; + } + + _aumid = aumid; + + var exePath = Process.GetCurrentProcess().MainModule?.FileName; + RegisterComServer(exePath); + + _registeredAumidAndComServer = true; + } + + private static void RegisterComServer(string exePath) + where T : NotificationActivator + { + // We register the EXE to start up when the notification is activated + var regString = $"SOFTWARE\\Classes\\CLSID\\{{{typeof(T).GUID}}}\\LocalServer32"; + 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. + /// + /// + public static ToastNotifier CreateToastNotifier() + { + EnsureRegistered(); + + return _aumid != null + ? ToastNotificationManager.CreateToastNotifier(_aumid) // non Desktop-Bridge + : 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(_aumid); + } + } + + 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. + /// + // ReSharper disable UnusedMember.Global + public static bool CanUseHttpImages => DesktopBridgeHelpers.IsRunningAsUwp(); + // ReSharper restore UnusedMember.Global + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/Base/INotificationActivationCallback.cs b/DFAssist.Core/Toast/Base/INotificationActivationCallback.cs new file mode 100644 index 0000000..f91f83b --- /dev/null +++ b/DFAssist.Core/Toast/Base/INotificationActivationCallback.cs @@ -0,0 +1,44 @@ +// ****************************************************************** +// 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.Runtime.InteropServices; + +namespace DFAssist.Core.Toast.Base +{ + [StructLayout(LayoutKind.Sequential), Serializable] + // ReSharper disable InconsistentNaming + public struct NOTIFICATION_USER_INPUT_DATA + + { + [MarshalAs(UnmanagedType.LPWStr)] + public string Key; + + [MarshalAs(UnmanagedType.LPWStr)] + public string Value; + } + // ReSharper restore InconsistentNaming + + [ComImport, Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface INotificationActivationCallback + { + void Activate( + [In, MarshalAs(UnmanagedType.LPWStr)] + string appUserModelId, + [In, MarshalAs(UnmanagedType.LPWStr)] + string invokedArgs, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] + NOTIFICATION_USER_INPUT_DATA[] data, + [In, MarshalAs(UnmanagedType.U4)] + uint dataCount); + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/Base/NotificationActivator.cs b/DFAssist.Core/Toast/Base/NotificationActivator.cs new file mode 100644 index 0000000..5364640 --- /dev/null +++ b/DFAssist.Core/Toast/Base/NotificationActivator.cs @@ -0,0 +1,33 @@ +// ****************************************************************** +// 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. +// ****************************************************************** + +namespace DFAssist.Core.Toast.Base +{ + /// + /// Apps must implement this activator to handle notification activation. + /// + public abstract class NotificationActivator : INotificationActivationCallback + { + public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount) + { + OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId); + } + + /// + /// This method will be called when the user clicks on a foreground or background activation on a toast. Parent app must implement this method. + /// + /// The arguments from the original notification. This is either the launch argument if the user clicked the body of your toast, or the arguments from a button on your toast. + /// Text and selection values that the user entered in your toast. + /// Your AUMID. + public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId); + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/Base/NotificationUserInput.cs b/DFAssist.Core/Toast/Base/NotificationUserInput.cs new file mode 100644 index 0000000..26fe07b --- /dev/null +++ b/DFAssist.Core/Toast/Base/NotificationUserInput.cs @@ -0,0 +1,69 @@ +// ****************************************************************** +// 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.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace DFAssist.Core.Toast.Base +{ + /// + /// Text and selection values that the user entered on your notification. The Key is the ID of the input, and the Value is what the user entered. + /// + public class NotificationUserInput : IReadOnlyDictionary + { + private readonly NOTIFICATION_USER_INPUT_DATA[] _data; + + internal NotificationUserInput(NOTIFICATION_USER_INPUT_DATA[] data) + { + _data = data; + } + + public string this[string key] => _data.First(i => i.Key == key).Value; + + public IEnumerable Keys => _data.Select(i => i.Key); + + public IEnumerable Values => _data.Select(i => i.Value); + + public int Count => _data.Length; + + public bool ContainsKey(string key) + { + return _data.Any(i => i.Key == key); + } + + public IEnumerator> GetEnumerator() + { + return _data.Select(i => new KeyValuePair(i.Key, i.Value)).GetEnumerator(); + } + + public bool TryGetValue(string key, out string value) + { + foreach (var item in _data) + { + if (item.Key == key) + { + value = item.Value; + return true; + } + } + + value = null; + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/ToastManager.cs b/DFAssist.Core/Toast/ToastManager.cs new file mode 100644 index 0000000..e682b61 --- /dev/null +++ b/DFAssist.Core/Toast/ToastManager.cs @@ -0,0 +1,48 @@ +using System; +using Windows.Data.Xml.Dom; +using Windows.UI.Notifications; +using DFAssist.Core.Toast.Base; +using Splat; + +namespace DFAssist.Core.Toast +{ + public class ToastManager + { + public static void ShowToast(string title, string message, string testing = null) + { + var logger = Locator.Current.GetService(); + try + { + if (!string.IsNullOrWhiteSpace(testing)) + testing = $"\nCode [{testing}]"; + + var toastXmlString = + $@" + + + {title} + {message} + {testing} + DFAssist + + + " + .Replace("\r\n", string.Empty) + .Replace("\t", string.Empty); + + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(toastXmlString); + var toast = new ToastNotification(xmlDoc); + + var toastNotifier = DesktopNotificationManagerCompat.CreateToastNotifier(); + toastNotifier.Show(toast); + logger.Write("UI: Toast Showing!", LogLevel.Debug); + } + catch (Exception e) + { + logger.Write(e, "UI: Unable to show the toast...", LogLevel.Error); + throw; + } + } + } +} diff --git a/DFAssist.Core/Toast/ToastNotificationActivator.cs b/DFAssist.Core/Toast/ToastNotificationActivator.cs new file mode 100644 index 0000000..6938053 --- /dev/null +++ b/DFAssist.Core/Toast/ToastNotificationActivator.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; +using DFAssist.Core.Toast.Base; +using Splat; + +namespace DFAssist.Core.Toast +{ + [ClassInterface(ClassInterfaceType.None)] + [ComSourceInterfaces(typeof(INotificationActivationCallback))] + [Guid("67d5ccfb-c77f-4a77-a37d-ecce57279150"), ComVisible(true)] + public class ToastNotificationActivator : NotificationActivator + { + public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId) + { + var logger = Locator.Current.GetService(); + logger.Write("UI: Toast Activated...", LogLevel.Debug); + } + } +} \ No newline at end of file diff --git a/DFAssist.Core/Toast/WinToastWrapper.cs b/DFAssist.Core/Toast/WinToastWrapper.cs deleted file mode 100644 index 334f6a5..0000000 --- a/DFAssist.Core/Toast/WinToastWrapper.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace DFAssist.Core.Toast -{ - public enum Duration - { - // ReSharper disable InconsistentNaming - // ReSharper disable UnusedMember.Global - System = 0, - Short, - Long - // ReSharper restore UnusedMember.Global - // ReSharper restore InconsistentNaming - }; - - public enum AudioOption - { - // ReSharper disable InconsistentNaming - // ReSharper disable UnusedMember.Global - Default = 0, - Silent, - Loop - // ReSharper restore UnusedMember.Global - // ReSharper restore InconsistentNaming - }; - - public enum AudioSystemFile - { - // ReSharper disable InconsistentNaming - // ReSharper disable UnusedMember.Global - DefaultSound = 0, - IM, - Mail, - Reminder, - SMS, - Alarm, - Alarm2, - Alarm3, - Alarm4, - Alarm5, - Alarm6, - Alarm7, - Alarm8, - Alarm9, - Alarm10, - Call, - Call1, - Call2, - Call3, - Call4, - Call5, - Call6, - Call7, - Call8, - Call9, - Call10, - // ReSharper restore UnusedMember.Global - // ReSharper restore InconsistentNaming - }; - - public static class WinToastWrapper - { - private const string DllFilePath = "DFAssist.WinToast.dll"; - - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern bool SetDllDirectory(string lpPathName); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void ToastEventCallback(int messageCode); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_Text01", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_Text02", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastTitle, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - bool wrapFirstLine = true, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_Text03", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastTitle, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.LPWStr)]string toastAdditionalMessage, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_ImageAndText01", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.LPWStr)]string toastImagePath, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_ImageAndText02", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastTitle, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.LPWStr)]string toastImagePath, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - bool wrapFirstLine = true, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - - [DllImport(DllFilePath, EntryPoint = "CreateToast_ImageAndText03", ExactSpelling = true)] - public static extern void CreateToast( - [MarshalAs(UnmanagedType.LPWStr)]string appName, - [MarshalAs(UnmanagedType.LPWStr)]string appUserModelId, - [MarshalAs(UnmanagedType.LPWStr)]string toastTitle, - [MarshalAs(UnmanagedType.LPWStr)]string toastMessage, - [MarshalAs(UnmanagedType.LPWStr)]string toastAdditionalMessage, - [MarshalAs(UnmanagedType.LPWStr)]string toastImagePath, - [MarshalAs(UnmanagedType.FunctionPtr)]ToastEventCallback eventCallback, - [MarshalAs(UnmanagedType.LPWStr)]string attribution = null, - [MarshalAs(UnmanagedType.I4)]Duration duration = Duration.System, - [MarshalAs(UnmanagedType.I4)]AudioSystemFile audioFile = AudioSystemFile.DefaultSound, - [MarshalAs(UnmanagedType.I4)]AudioOption audioOption = AudioOption.Default); - } -} diff --git a/DFAssist.Loader/AssemblyResolver.cs b/DFAssist.Loader/AssemblyResolver.cs index 233eeee..54e8134 100644 --- a/DFAssist.Loader/AssemblyResolver.cs +++ b/DFAssist.Loader/AssemblyResolver.cs @@ -110,8 +110,7 @@ private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs a if (requestingAssemblyName != "DFAssist" && requestingAssemblyName != "DFAssist.Plugin" && requestingAssemblyName != "DFAssist.Core" - && requestingAssemblyName != "DFAssist.Contracts" - && requestingAssemblyName != "DFAssist.WinToast") + && requestingAssemblyName != "DFAssist.Contracts") return null; var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); diff --git a/DFAssist.WinToast/DFAssist.WinToast.rc b/DFAssist.WinToast/DFAssist.WinToast.rc deleted file mode 100644 index 850e337..0000000 Binary files a/DFAssist.WinToast/DFAssist.WinToast.rc and /dev/null differ diff --git a/DFAssist.WinToast/DFAssist.WinToast.vcxproj b/DFAssist.WinToast/DFAssist.WinToast.vcxproj deleted file mode 100644 index f262148..0000000 --- a/DFAssist.WinToast/DFAssist.WinToast.vcxproj +++ /dev/null @@ -1,181 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61} - Win32Proj - DFAssistWinToast - 10.0 - - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - ..\Release\libs\ - - - true - - - false - - - false - ..\Release\libs\ - - - - Use - Level3 - Disabled - true - _DEBUG;DFASSISTWINTOAST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - - - Windows - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;WindowsApp.lib;%(AdditionalDependencies) - MachineX64 - - - - - Use - Level3 - Disabled - true - WIN32;_DEBUG;DFASSISTWINTOAST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - - - Windows - true - - - - - Use - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;DFASSISTWINTOAST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;DFASSISTWINTOAST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;WindowsApp.lib;%(AdditionalDependencies) - UseLinkTimeCodeGeneration - MachineX64 - - - - - - - - - - - - - - Create - Create - Create - Create - - - - - - - - - - - \ No newline at end of file diff --git a/DFAssist.WinToast/DFAssist.WinToast.vcxproj.filters b/DFAssist.WinToast/DFAssist.WinToast.vcxproj.filters deleted file mode 100644 index 2a72042..0000000 --- a/DFAssist.WinToast/DFAssist.WinToast.vcxproj.filters +++ /dev/null @@ -1,56 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/DFAssist.WinToast/dllmain.cpp b/DFAssist.WinToast/dllmain.cpp deleted file mode 100644 index 465ae72..0000000 --- a/DFAssist.WinToast/dllmain.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// dllmain.cpp : Defines the entry point for the DLL application. -#include "stdafx.h" - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - diff --git a/DFAssist.WinToast/resource.h b/DFAssist.WinToast/resource.h deleted file mode 100644 index 3caecb4..0000000 --- a/DFAssist.WinToast/resource.h +++ /dev/null @@ -1,14 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by DFAssist.WinToast.rc - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/DFAssist.WinToast/stdafx.cpp b/DFAssist.WinToast/stdafx.cpp deleted file mode 100644 index fd4f341..0000000 --- a/DFAssist.WinToast/stdafx.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "stdafx.h" diff --git a/DFAssist.WinToast/stdafx.h b/DFAssist.WinToast/stdafx.h deleted file mode 100644 index f380517..0000000 --- a/DFAssist.WinToast/stdafx.h +++ /dev/null @@ -1,16 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#include "targetver.h" - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files -#include - - - -// reference additional headers your program requires here diff --git a/DFAssist.WinToast/targetver.h b/DFAssist.WinToast/targetver.h deleted file mode 100644 index 87c0086..0000000 --- a/DFAssist.WinToast/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include diff --git a/DFAssist.WinToast/version.h b/DFAssist.WinToast/version.h deleted file mode 100644 index 3c1b8e1..0000000 --- a/DFAssist.WinToast/version.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#define STRINGIZE2(s) #s -#define STRINGIZE(s) STRINGIZE2(s) - -// versions must be updated manually, based on the version of the main assembly -#define VERSION_MAJOR 2 -#define VERSION_MINOR 0 -#define VERSION_REVISION 0 -#define VERSION_BUILD 0 - -#if DEBUG -#define VER_FILE_DESCRIPTION_STR "Flavor=Debug" -#else -#define VER_FILE_DESCRIPTION_STR "Flavor=Retail" -#endif -#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD -#define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ - "." STRINGIZE(VERSION_MINOR) \ - "." STRINGIZE(VERSION_REVISION) \ - "." STRINGIZE(VERSION_BUILD) \ - -#define VER_PRODUCTNAME_STR "DFAssist.WinToast" -#define VER_PRODUCT_VERSION VER_FILE_VERSION -#define VER_PRODUCT_VERSION_STR VER_FILE_VERSION_STR -#define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".dll" -#define VER_INTERNAL_NAME_STR VER_ORIGINAL_FILENAME_STR -#define VER_COPYRIGHT_STR "Copyright Carlo Ruggiero (easly1989) 2018/2019" - -#ifdef _DEBUG - #define VER_VER_DEBUG VS_FF_DEBUG -#else - #define VER_VER_DEBUG 0 -#endif - -#define VER_FILEOS VOS_NT_WINDOWS32 -#define VER_FILEFLAGS VER_VER_DEBUG -#define VER_FILETYPE VFT_APP diff --git a/DFAssist.WinToast/wintoastimpl.cpp b/DFAssist.WinToast/wintoastimpl.cpp deleted file mode 100644 index d528f74..0000000 --- a/DFAssist.WinToast/wintoastimpl.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "stdafx.h" -#include "wintoastimpl.h" -#include - -using namespace WinToastLib; - -class WinToastHandler : public IWinToastHandler { -public: - - explicit WinToastHandler(ToastEventCallback eventCallback) - { - m_eventCallback = eventCallback; - } - - void toastActivated() const override - { - std::wcout << L"The user clicked in this toast" << std::endl; - m_eventCallback(0); - } - - void toastActivated(int actionIndex) const override - { - std::wcout << L"The user clicked on action #" << actionIndex << std::endl; - m_eventCallback(16 + actionIndex); - } - - void toastDismissed(WinToastDismissalReason state) const override - { - switch (state) { - case UserCanceled: - std::wcout << L"The user dismissed this toast" << std::endl; - m_eventCallback(1); - break; - case TimedOut: - std::wcout << L"The toast has timed out" << std::endl; - m_eventCallback(2); - break; - case ApplicationHidden: - std::wcout << L"The application hid the toast using ToastNotifier.hide()" << std::endl; - m_eventCallback(3); - break; - default: - std::wcout << L"Toast not activated" << std::endl; - m_eventCallback(4); - break; - } - } - - void toastFailed() const override - { - std::wcout << L"Error showing current toast" << std::endl; - m_eventCallback(5); - } -private: - ToastEventCallback m_eventCallback; -}; - -// -------------------------- Exported methods -DLLEXPORT void CreateToast_Text01(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - WinToastTemplate::WinToastTemplateType::Text01, - eventCallback, - toastMessage, - nullptr, - nullptr, - nullptr, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -DLLEXPORT void CreateToast_Text02(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution, - bool wrapFirstLine, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - wrapFirstLine ? WinToastTemplate::WinToastTemplateType::Text02 : WinToastTemplate::WinToastTemplateType::Text03, - eventCallback, - toastTitle, - toastMessage, - nullptr, - nullptr, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -DLLEXPORT void CreateToast_Text03(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastAdditionalMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - WinToastTemplate::WinToastTemplateType::Text04, - eventCallback, - toastTitle, - toastMessage, - toastAdditionalMessage, - nullptr, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -DLLEXPORT void CreateToast_ImageAndText01(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - WinToastTemplate::WinToastTemplateType::ImageAndText01, - eventCallback, - toastMessage, - nullptr, - nullptr, - toastImagePath, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -DLLEXPORT void CreateToast_ImageAndText02(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution, - bool wrapFirstLine, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - wrapFirstLine ? WinToastTemplate::WinToastTemplateType::ImageAndText02 : WinToastTemplate::WinToastTemplateType::ImageAndText03, - eventCallback, - toastTitle, - toastMessage, - nullptr, - toastImagePath, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -DLLEXPORT void CreateToast_ImageAndText03(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastAdditionalMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution, - int duration, - int audioFile, - int audioOption) -{ - CreateToast( - appName, - appUserModelID, - WinToastTemplate::WinToastTemplateType::ImageAndText04, - eventCallback, - toastTitle, - toastMessage, - toastAdditionalMessage, - toastImagePath, - attribution, - static_cast(duration), - static_cast(audioFile), - static_cast(audioOption)); -} - -void CreateToast(const wchar_t* appName, - const wchar_t* appUserModelID, - WinToastTemplate::WinToastTemplateType toastTemplate, - ToastEventCallback eventCallback, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastAdditionlMessage, - const wchar_t* toastImagePath, - const wchar_t* attribution, - WinToastTemplate::Duration duration, - WinToastTemplate::AudioSystemFile audioFile, - WinToastTemplate::AudioOption audioOption, - INT64 expiration, - bool createShortcut) { - - if (!WinToast::isCompatible()) { - std::wcerr << L"Error, your system in not supported!" << std::endl; - eventCallback(SystemNotSupported); - } - - WinToast::instance()->setAppName(appName); - WinToast::instance()->setAppUserModelId(appUserModelID); - - if (createShortcut) { - const auto shortcutResult = WinToast::instance()->createShortcut(); - if (shortcutResult < 0) - eventCallback(InitializationFailure); - } - - if (!WinToast::instance()->initialize()) { - std::wcerr << L"Error, your system in not compatible!" << std::endl; - eventCallback(InitializationFailure); - } - - WinToastTemplate templ(toastTemplate); - - if (toastTitle) - templ.setFirstLine(toastTitle); - if (toastMessage) - templ.setSecondLine(toastMessage); - if (toastAdditionlMessage) - templ.setThirdLine(toastAdditionlMessage); - if (duration) - templ.setDuration(duration); - if (audioFile) - templ.setAudioPath(audioFile); - if (audioOption) - templ.setAudioOption(audioOption); - if (attribution) - templ.setAttributionText(attribution); - if (toastImagePath) - templ.setImagePath(toastImagePath); - - if (expiration > 0) - templ.setExpiration(expiration); - - if (WinToast::instance()->showToast(templ, new WinToastHandler(eventCallback)) < 0) { - std::wcerr << L"Could not launch your toast notification!"; - eventCallback(ToastFailed); - } - - eventCallback(AwaitingInteractions); -} \ No newline at end of file diff --git a/DFAssist.WinToast/wintoastimpl.h b/DFAssist.WinToast/wintoastimpl.h deleted file mode 100644 index 8c0caf7..0000000 --- a/DFAssist.WinToast/wintoastimpl.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include "wintoastlib.h" - -#define DLLEXPORT extern "C" __declspec(dllexport) - -using namespace WinToastLib; - -typedef void(*ToastEventCallback)(int messageCode); - -enum Results { - ToastClicked, // user clicked on the toast - ToastDismissed, // user dismissed the toast - ToastTimeOut, // toast timed out - ToastHided, // application hid the toast - ToastNotActivated, // toast was not activated - ToastFailed, // toast failed - SystemNotSupported, // system does not support toasts - UnhandledOption, // unhandled option - MultipleTextNotSupported, // multiple texts were provided - InitializationFailure, // toast notification manager initialization failure - ToastNotLaunched, // toast could not be launched, - AwaitingInteractions // when the toast is shown, awaiting for user interaction, timeone or an eventual error -}; - -DLLEXPORT void CreateToast_Text01(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -DLLEXPORT void CreateToast_Text02(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - bool wrapFirstLine = true, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -DLLEXPORT void CreateToast_Text03(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastAdditionalMessage, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -DLLEXPORT void CreateToast_ImageAndText01(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -DLLEXPORT void CreateToast_ImageAndText02(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - bool wrapFirstLine = true, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -DLLEXPORT void CreateToast_ImageAndText03(const wchar_t* appName, - const wchar_t* appUserModelID, - const wchar_t* toastTitle, - const wchar_t* toastMessage, - const wchar_t* toastAdditionalMessage, - const wchar_t* toastImagePath, - ToastEventCallback eventCallback, - const wchar_t* attribution = nullptr, - int duration = 0, - int audioFile = 0, - int audioOption = 0); - -// internal call, to generate every toast -void CreateToast(const wchar_t* appName, - const wchar_t* appUserModelID, - WinToastTemplate::WinToastTemplateType toastTemplate, - ToastEventCallback eventCallback, - const wchar_t* toastTitle = nullptr, - const wchar_t* toastMessage = nullptr, - const wchar_t* toastAdditionlMessage = nullptr, - const wchar_t* toastImagePath = nullptr, - const wchar_t* attribution = nullptr, - WinToastTemplate::Duration duration = WinToastTemplate::Duration::System, - WinToastTemplate::AudioSystemFile audioFile = WinToastTemplate::AudioSystemFile::DefaultSound, - WinToastTemplate::AudioOption audioOption = WinToastTemplate::AudioOption::Default, - INT64 expiration = -1, - bool createShortcut = true); \ No newline at end of file diff --git a/DFAssist.WinToast/wintoastlib.cpp b/DFAssist.WinToast/wintoastlib.cpp deleted file mode 100644 index 4ff4d30..0000000 --- a/DFAssist.WinToast/wintoastlib.cpp +++ /dev/null @@ -1,1131 +0,0 @@ -/* * Copyright (C) 2016-2019 Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "stdafx.h" -#include "wintoastlib.h" -#include -#include -#include -#include - -#pragma comment(lib,"shlwapi") -#pragma comment(lib,"user32") - -#ifdef NDEBUG - #define DEBUG_MSG(str) do { } while ( false ) -#else - #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false ) -#endif - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -#define STATUS_SUCCESS (0x00000000) - - -// Quickstart: Handling toast activations from Win32 apps in Windows 10 -// https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ -using namespace WinToastLib; -namespace DllImporter { - - // Function load a function from library - template - HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { - if (!library) { - return E_INVALIDARG; - } - func = reinterpret_cast(GetProcAddress(library, name)); - return (func != nullptr) ? S_OK : E_FAIL; - } - - typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); - typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_ UINT32 *length); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); - - static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; - static f_PropVariantToString PropVariantToString; - static f_RoGetActivationFactory RoGetActivationFactory; - static f_WindowsCreateStringReference WindowsCreateStringReference; - static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; - static f_WindowsDeleteString WindowsDeleteString; - - - template - _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { - return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); - } - - template - inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); - } - - inline HRESULT initialize() { - HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); - HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); - if (SUCCEEDED(hr)) { - HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); - if (SUCCEEDED(hr)) { - HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); - const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); - return succeded ? S_OK : E_FAIL; - } - } - return hr; - } -} - -class WinToastStringWrapper { -public: - WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); - if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - ~WinToastStringWrapper() { - DllImporter::WindowsDeleteString(_hstring); - } - - inline HSTRING Get() const noexcept { - return _hstring; - } -private: - HSTRING _hstring; - HSTRING_HEADER _header; - -}; - -class InternalDateTime : public IReference { -public: - static INT64 Now() { - FILETIME now; - GetSystemTimeAsFileTime(&now); - return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); - } - - InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} - - InternalDateTime(INT64 millisecondsFromNow) { - _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; - } - - virtual ~InternalDateTime() = default; - - operator INT64() { - return _dateTime.UniversalTime; - } - - HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) { - *dateTime = _dateTime; - return S_OK; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) { - if (!ppvObject) { - return E_POINTER; - } - if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference)) { - *ppvObject = static_cast(static_cast*>(this)); - return S_OK; - } - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE Release() { - return 1; - } - - ULONG STDMETHODCALLTYPE AddRef() { - return 2; - } - - HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) { - return E_NOTIMPL; - } - -protected: - DateTime _dateTime; -}; - -namespace Util { - - typedef LONG NTSTATUS, *PNTSTATUS; - typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); - inline RTL_OSVERSIONINFOW getRealOSVersion() { - HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); - if (hMod) { - RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); - if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = { 0 }; - rovi.dwOSVersionInfoSize = sizeof(rovi); - if (STATUS_SUCCESS == fxPtr(&rovi)) { - return rovi; - } - } - } - RTL_OSVERSIONINFOW rovi = { 0 }; - return rovi; - } - - inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); - DEBUG_MSG("Default executable path: " << path); - return (written > 0) ? S_OK : E_FAIL; - } - - - inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link path: " << path); - } - return hr; - } - - inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link file path: " << path); - } - return hr; - } - - - inline PCWSTR AsString(ComPtr &xmlDocument) { - HSTRING xml; - ComPtr ser; - HRESULT hr = xmlDocument.As(&ser); - hr = ser->GetXml(&xml); - if (SUCCEEDED(hr)) - return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); - return nullptr; - } - - inline PCWSTR AsString(HSTRING hstring) { - return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); - } - - inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) { - ComPtr textNode; - HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); - if (SUCCEEDED(hr)) { - ComPtr stringNode; - hr = textNode.As(&stringNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = node->AppendChild(stringNode.Get(), &appendedChild); - } - } - return hr; - } - - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime) { - EventRegistrationToken activatedToken, dismissedToken, failedToken; - HRESULT hr = notification->add_Activated( - Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler](IToastNotification*, IInspectable* inspectable) - { - IToastActivatedEventArgs *activatedEventArgs; - HRESULT hr = inspectable->QueryInterface(&activatedEventArgs); - if (SUCCEEDED(hr)) { - HSTRING argumentsHandle; - hr = activatedEventArgs->get_Arguments(&argumentsHandle); - if (SUCCEEDED(hr)) { - PCWSTR arguments = Util::AsString(argumentsHandle); - if (arguments && *arguments) { - eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); - return S_OK; - } - } - } - eventHandler->toastActivated(); - return S_OK; - }).Get(), &activatedToken); - - if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler, expirationTime](IToastNotification*, IToastDismissedEventArgs* e) - { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) - { - if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime) - reason = ToastDismissalReason_TimedOut; - eventHandler->toastDismissed(static_cast(reason)); - } - return S_OK; - }).Get(), &dismissedToken); - if (SUCCEEDED(hr)) { - hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler](IToastNotification*, IToastFailedEventArgs*) - { - eventHandler->toastFailed(); - return S_OK; - }).Get(), &failedToken); - } - } - return hr; - } - - inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) { - ComPtr srcAttribute; - HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = srcAttribute.As(&node); - if (SUCCEEDED(hr)) { - ComPtr pNode; - hr = attributeMap->SetNamedItem(node.Get(), &pNode); - } - } - return hr; - } - - inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector& attribute_names) { - ComPtr rootList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); - if (SUCCEEDED(hr)) { - ComPtr root; - hr = rootList->Item(0, &root); - if (SUCCEEDED(hr)) { - ComPtr audioElement; - hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); - if (SUCCEEDED(hr)) { - ComPtr audioNodeTmp; - hr = audioElement.As(&audioNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr audioNode; - hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = audioNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - for (const auto& it : attribute_names) { - hr = addAttribute(xml, it, attributes.Get()); - } - } - } - } - } - } - } - return hr; - } -} - -WinToast* WinToast::instance() { - static WinToast instance; - return &instance; -} - -WinToast::WinToast() : - _isInitialized(false), - _hasCoInitialized(false) -{ - if (!isCompatible()) { - DEBUG_MSG(L"Warning: Your system is not compatible with this library "); - } -} - -WinToast::~WinToast() { - if (_hasCoInitialized) { - CoUninitialize(); - } -} - -void WinToast::setAppName(_In_ const std::wstring& appName) { - _appName = appName; -} - - -void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { - _aumi = aumi; - DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); -} - -bool WinToast::isCompatible() { - DllImporter::initialize(); - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) - || (DllImporter::PropVariantToString == nullptr) - || (DllImporter::RoGetActivationFactory == nullptr) - || (DllImporter::WindowsCreateStringReference == nullptr) - || (DllImporter::WindowsDeleteString == nullptr)); -} - -bool WinToastLib::WinToast::isSupportingModernFeatures() { - constexpr auto MinimumSupportedVersion = 6; - return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; - -} -std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, - _In_ const std::wstring &productName, - _In_ const std::wstring &subProduct, - _In_ const std::wstring &versionInformation) -{ - std::wstring aumi = companyName; - aumi += L"." + productName; - if (subProduct.length() > 0) { - aumi += L"." + subProduct; - if (versionInformation.length() > 0) { - aumi += L"." + versionInformation; - } - } - - if (aumi.length() > SCHAR_MAX) { - DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); - } - return aumi; -} - -const std::wstring& WinToast::strerror(WinToastError error) { - static const std::unordered_map Labels = { - {WinToastError::NoError, L"No error. The process was executed correctly"}, - {WinToastError::NotInitialized, L"The library has not been initialized"}, - {WinToastError::SystemNotSupported, L"The OS does not support WinToast"}, - {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"}, - {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"}, - {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"}, - {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, - {WinToastError::UnknownError, L"Unknown error"} - }; - - const auto iter = Labels.find(error); - assert(iter != Labels.end()); - return iter->second; -} - -enum WinToast::ShortcutResult WinToast::createShortcut() { - if (_aumi.empty() || _appName.empty()) { - DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); - return SHORTCUT_MISSING_PARAMETERS; - } - - if (!isCompatible()) { - DEBUG_MSG(L"Your OS is not compatible with this library! =("); - return SHORTCUT_INCOMPATIBLE_OS; - } - - if (!_hasCoInitialized) { - HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); - if (initHr != RPC_E_CHANGED_MODE) { - if (FAILED(initHr) && initHr != S_FALSE) { - DEBUG_MSG(L"Error on COM library initialization!"); - return SHORTCUT_COM_INIT_FAILURE; - } - else { - _hasCoInitialized = true; - } - } - } - - bool wasChanged; - HRESULT hr = validateShellLinkHelper(wasChanged); - if (SUCCEEDED(hr)) - return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; - - hr = createShellLinkHelper(); - return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; -} - -bool WinToast::initialize(_Out_ WinToastError* error) { - _isInitialized = false; - setError(error, WinToastError::NoError); - - if (!isCompatible()) { - setError(error, WinToastError::SystemNotSupported); - DEBUG_MSG(L"Error: system not supported."); - return false; - } - - - if (_aumi.empty() || _appName.empty()) { - setError(error, WinToastError::InvalidParameters); - DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); - return false; - } - - if (createShortcut() < 0) { - setError(error, WinToastError::ShellLinkNotCreated); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - - if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { - setError(error, WinToastError::InvalidAppUserModelID); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - - _isInitialized = true; - return _isInitialized; -} - -bool WinToast::isInitialized() const { - return _isInitialized; -} - -const std::wstring& WinToast::appName() const { - return _appName; -} - -const std::wstring& WinToast::appUserModelId() const { - return _aumi; -} - - -HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { - WCHAR path[MAX_PATH] = { L'\0' }; - Util::defaultShellLinkPath(_appName, path); - // Check if the file exist - DWORD attr = GetFileAttributesW(path); - if (attr >= 0xFFFFFFF) { - DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); - return E_FAIL; - } - - // Let's load the file as shell link to validate. - // - Create a shell link - // - Create a persistant file - // - Load the path as data for the persistant file - // - Read the property AUMI and validate with the current - // - Review if AUMI is equal. - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Load(path, STGM_READWRITE); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); - if (SUCCEEDED(hr)) { - WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); - wasChanged = false; - if (FAILED(hr) || _aumi != AUMI) { - // AUMI Changed for the same app, let's update the current value! =) - wasChanged = true; - PropVariantClear(&appIdPropVar); - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(path, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - return hr; -} - - - -HRESULT WinToast::createShellLinkHelper() { - WCHAR exePath[MAX_PATH]{L'\0'}; - WCHAR slPath[MAX_PATH]{L'\0'}; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); - if (SUCCEEDED(hr)) { - hr = shellLink->SetArguments(L""); - if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exePath); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - } - return hr; -} - -INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) { - setError(error, WinToastError::NoError); - INT64 id = -1; - if (!isInitialized()) { - setError(error, WinToastError::NotInitialized); - DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); - return id; - } - if (!handler) { - setError(error, WinToastError::InvalidHandler); - DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr."); - return id; - } - - ComPtr notificationManager; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - ComPtr notifier; - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - if (SUCCEEDED(hr)) { - ComPtr notificationFactory; - hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); - if (SUCCEEDED(hr)) { - ComPtr xmlDocument; - HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); - if (SUCCEEDED(hr)) { - for (size_t i = 0, fieldsCount = toast.textFieldsCount(); i < fieldsCount && SUCCEEDED(hr); i++) { - const auto position = static_cast(i); - hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), position); - } - - // Modern feature are supported Windows > Windows 10 - if (SUCCEEDED(hr) && isSupportingModernFeatures()) { - - // Note that we do this *after* using toast.textFieldsCount() to - // iterate/fill the template's text fields, since we're adding yet another text field. - if (SUCCEEDED(hr) - && !toast.attributionText().empty()) { - hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); - } - - std::array buf; - for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) { - _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i); - hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data()); - } - - if (SUCCEEDED(hr)) { - hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) - ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); - } - - if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { - hr = addDurationHelper(xmlDocument.Get(), - (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); - } - - } else { - DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); - } - - if (SUCCEEDED(hr)) { - hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr; - if (SUCCEEDED(hr)) { - ComPtr notification; - hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); - if (SUCCEEDED(hr)) { - INT64 expiration = 0, relativeExpiration = toast.expiration(); - if (relativeExpiration > 0) { - InternalDateTime expirationDateTime(relativeExpiration); - expiration = expirationDateTime; - hr = notification->put_ExpirationTime(&expirationDateTime); - } - - if (SUCCEEDED(hr)) { - hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); - if (FAILED(hr)) { - setError(error, WinToastError::InvalidHandler); - } - } - - if (SUCCEEDED(hr)) { - GUID guid; - hr = CoCreateGuid(&guid); - if (SUCCEEDED(hr)) { - id = guid.Data1; - _buffer[id] = notification; - DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); - hr = notifier->Show(notification.Get()); - if (FAILED(hr)) { - setError(error, WinToastError::NotDisplayed); - } - } - } - } - } - } - } - } - } - } - return FAILED(hr) ? -1 : id; -} - -ComPtr WinToast::notifier(_In_ bool* succeded) const { - ComPtr notificationManager; - ComPtr notifier; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - } - *succeded = SUCCEEDED(hr); - return notifier; -} - -bool WinToast::hideToast(_In_ INT64 id) { - if (!isInitialized()) { - DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); - return false; - } - - if (_buffer.find(id) != _buffer.end()) { - auto succeded = false; - auto notify = notifier(&succeded); - if (succeded) { - auto result = notify->Hide(_buffer[id].Get()); - _buffer.erase(id); - return SUCCEEDED(result); - } - } - return false; -} - -void WinToast::clear() { - auto succeded = false; - auto notify = notifier(&succeded); - if (succeded) { - auto end = _buffer.end(); - for (auto it = _buffer.begin(); it != end; ++it) { - notify->Hide(it->second.Get()); - } - _buffer.clear(); - } -} - -// -// Available as of Windows 10 Anniversary Update -// Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts -// -// NOTE: This will add a new text field, so be aware when iterating over -// the toast's text fields or getting a count of them. -// -HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) { - Util::createElement(xml, L"binding", L"text", { L"placement" }); - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 nodeListLength; - hr = nodeList->get_Length(&nodeListLength); - if (SUCCEEDED(hr)) { - for (UINT32 i = 0; i < nodeListLength; i++) { - ComPtr textNode; - hr = nodeList->Item(i, &textNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = textNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); - if (FAILED(hr) || !editedNode) { - continue; - } - hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); - if (SUCCEEDED(hr)) { - return setTextFieldHelper(xml, text, i); - } - } - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), - WinToastStringWrapper(duration).Get()); - } - } - } - } - return hr; -} - -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(pos, &node); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(text, node.Get(), xml); - } - } - return hr; -} - - -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) { - assert(path.size() < MAX_PATH); - - wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); - if (SUCCEEDED(hr)) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(imagePath, editedNode.Get(), xml); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) { - std::vector attrs; - if (!path.empty()) attrs.push_back(L"src"); - if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop"); - if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent"); - Util::createElement(xml, L"toast", L"audio", attrs); - - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (!path.empty()) { - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(path, editedNode.Get(), xml); - } - } - } - - if (SUCCEEDED(hr)) { - switch (option) { - case WinToastTemplate::AudioOption::Loop: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - break; - case WinToastTemplate::AudioOption::Silent: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - default: - break; - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr actionsNode; - if (length > 0) { - hr = nodeList->Item(0, &actionsNode); - } else { - hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); - if (SUCCEEDED(hr)) { - ComPtr actionsElement; - hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); - if (SUCCEEDED(hr)) { - hr = actionsElement.As(&actionsNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); - } - } - } - } - } - } - } - if (SUCCEEDED(hr)) { - ComPtr actionElement; - hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) - hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); - if (SUCCEEDED(hr)) - hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); - if (SUCCEEDED(hr)) { - ComPtr actionNode; - hr = actionElement.As(&actionNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); - } - } - } - } - } - return hr; -} - -void WinToast::setError(_Out_ WinToastError* error, _In_ WinToastError value) { - if (error) { - *error = value; - } -} - -WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { - static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[type], L""); -} - -WinToastTemplate::~WinToastTemplate() { - _textFields.clear(); -} - -void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { - const auto position = static_cast(pos); - assert(position < _textFields.size()); - _textFields[position] = txt; -} - -void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { - _imagePath = imgPath; -} - -void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) { - _audioPath = audioPath; -} - -void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { - static const std::unordered_map Files = { - {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"}, - {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"}, - {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"}, - {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"}, - {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"}, - {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"}, - {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"}, - {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"}, - {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"}, - {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"}, - {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"}, - {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"}, - {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"}, - {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"}, - {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, - {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"}, - {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"}, - {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"}, - {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"}, - {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"}, - {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"}, - {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"}, - {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"}, - {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"}, - {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"}, - {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"}, - }; - const auto iter = Files.find(file); - assert(iter != Files.end()); - _audioPath = iter->second; -} - -void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { - _audioOption = audioOption; -} - -void WinToastTemplate::setFirstLine(const std::wstring &text) { - setTextField(text, WinToastTemplate::FirstLine); -} - -void WinToastTemplate::setSecondLine(const std::wstring &text) { - setTextField(text, WinToastTemplate::SecondLine); -} - -void WinToastTemplate::setThirdLine(const std::wstring &text) { - setTextField(text, WinToastTemplate::ThirdLine); -} - -void WinToastTemplate::setDuration(_In_ Duration duration) { - _duration = duration; -} - -void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { - _expiration = millisecondsFromNow; -} - -void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) { - _attributionText = attributionText; -} - -void WinToastTemplate::addAction(_In_ const std::wstring & label) { - _actions.push_back(label); -} - -std::size_t WinToastTemplate::textFieldsCount() const { - return _textFields.size(); -} - -std::size_t WinToastTemplate::actionsCount() const { - return _actions.size(); -} - -bool WinToastTemplate::hasImage() const { - return _type < WinToastTemplateType::Text01; -} - -const std::vector& WinToastTemplate::textFields() const { - return _textFields; -} - -const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { - const auto position = static_cast(pos); - assert(position < _textFields.size()); - return _textFields[position]; -} - -const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const { - assert(position < _actions.size()); - return _actions[position]; -} - -const std::wstring& WinToastTemplate::imagePath() const { - return _imagePath; -} - -const std::wstring& WinToastTemplate::audioPath() const { - return _audioPath; -} - -const std::wstring& WinToastTemplate::attributionText() const { - return _attributionText; -} - -INT64 WinToastTemplate::expiration() const { - return _expiration; -} - -WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { - return _type; -} - -WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { - return _audioOption; -} - -WinToastTemplate::Duration WinToastTemplate::duration() const { - return _duration; -} diff --git a/DFAssist.WinToast/wintoastlib.h b/DFAssist.WinToast/wintoastlib.h deleted file mode 100644 index f226e5f..0000000 --- a/DFAssist.WinToast/wintoastlib.h +++ /dev/null @@ -1,218 +0,0 @@ -/* * Copyright (C) 2016-2019 Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef WINTOASTLIB_H -#define WINTOASTLIB_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Microsoft::WRL; -using namespace ABI::Windows::Data::Xml::Dom; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::UI::Notifications; -using namespace Windows::Foundation; - - -namespace WinToastLib { - class IWinToastHandler { - public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason_UserCanceled, - ApplicationHidden = ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason_TimedOut - }; - virtual ~IWinToastHandler() = default; - virtual void toastActivated() const = 0; - virtual void toastActivated(int actionIndex) const = 0; - virtual void toastDismissed(WinToastDismissalReason state) const = 0; - virtual void toastFailed() const = 0; - }; - - class WinToastTemplate { - public: - enum Duration { System, Short, Long }; - enum AudioOption { Default = 0, Silent, Loop }; - enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - enum WinToastTemplateType { - ImageAndText01 = ToastTemplateType_ToastImageAndText01, - ImageAndText02 = ToastTemplateType_ToastImageAndText02, - ImageAndText03 = ToastTemplateType_ToastImageAndText03, - ImageAndText04 = ToastTemplateType_ToastImageAndText04, - Text01 = ToastTemplateType_ToastText01, - Text02 = ToastTemplateType_ToastText02, - Text03 = ToastTemplateType_ToastText03, - Text04 = ToastTemplateType_ToastText04, - }; - - enum AudioSystemFile { - DefaultSound, - IM, - Mail, - Reminder, - SMS, - Alarm, - Alarm2, - Alarm3, - Alarm4, - Alarm5, - Alarm6, - Alarm7, - Alarm8, - Alarm9, - Alarm10, - Call, - Call1, - Call2, - Call3, - Call4, - Call5, - Call6, - Call7, - Call8, - Call9, - Call10, - }; - - - WinToastTemplate(_In_ WinToastTemplateType type = ImageAndText02); - ~WinToastTemplate(); - - void setFirstLine(_In_ const std::wstring& text); - void setSecondLine(_In_ const std::wstring& text); - void setThirdLine(_In_ const std::wstring& text); - void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); - void setAttributionText(_In_ const std::wstring & attributionText); - void setImagePath(_In_ const std::wstring& imgPath); - void setAudioPath(_In_ AudioSystemFile audio); - void setAudioPath(_In_ const std::wstring& audioPath); - void setAudioOption(_In_ AudioOption audioOption); - void setDuration(_In_ Duration duration); - void setExpiration(_In_ INT64 millisecondsFromNow); - void addAction(_In_ const std::wstring& label); - - std::size_t textFieldsCount() const; - std::size_t actionsCount() const; - bool hasImage() const; - const std::vector& textFields() const; - const std::wstring& textField(_In_ TextField pos) const; - const std::wstring& actionLabel(_In_ std::size_t pos) const; - const std::wstring& imagePath() const; - const std::wstring& audioPath() const; - const std::wstring& attributionText() const; - INT64 expiration() const; - WinToastTemplateType type() const; - AudioOption audioOption() const; - Duration duration() const; - private: - std::vector _textFields{}; - std::vector _actions{}; - std::wstring _imagePath{}; - std::wstring _audioPath{}; - std::wstring _attributionText{}; - INT64 _expiration{ 0 }; - AudioOption _audioOption{ WinToastTemplate::AudioOption::Default }; - WinToastTemplateType _type{ WinToastTemplateType::Text01 }; - Duration _duration{ Duration::System }; - }; - - class WinToast { - public: - enum WinToastError { - NoError = 0, - NotInitialized, - SystemNotSupported, - ShellLinkNotCreated, - InvalidAppUserModelID, - InvalidParameters, - InvalidHandler, - NotDisplayed, - UnknownError - }; - - enum ShortcutResult { - SHORTCUT_UNCHANGED = 0, - SHORTCUT_WAS_CHANGED = 1, - SHORTCUT_WAS_CREATED = 2, - - SHORTCUT_MISSING_PARAMETERS = -1, - SHORTCUT_INCOMPATIBLE_OS = -2, - SHORTCUT_COM_INIT_FAILURE = -3, - SHORTCUT_CREATE_FAILED = -4 - }; - - WinToast(void); - virtual ~WinToast(); - static WinToast* instance(); - static bool isCompatible(); - static bool isSupportingModernFeatures(); - static std::wstring configureAUMI(_In_ const std::wstring& companyName, - _In_ const std::wstring& productName, - _In_ const std::wstring& subProduct = std::wstring(), - _In_ const std::wstring& versionInformation = std::wstring()); - static const std::wstring& strerror(_In_ WinToastError error); - virtual bool initialize(_Out_ WinToastError* error = nullptr); - virtual bool isInitialized() const; - virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error = nullptr); - virtual void clear(); - virtual enum ShortcutResult createShortcut(); - - const std::wstring& appName() const; - const std::wstring& appUserModelId() const; - void setAppUserModelId(_In_ const std::wstring& aumi); - void setAppName(_In_ const std::wstring& appName); - - protected: - bool _isInitialized{ false }; - bool _hasCoInitialized{ false }; - std::wstring _appName{}; - std::wstring _aumi{}; - std::map> _buffer{}; - - HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path); - HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); - HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); - HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); - ComPtr notifier(_In_ bool* succeded) const; - void setError(_Out_ WinToastError* error, _In_ WinToastError value); - }; -} -#endif // WINTOASTLIB_H - diff --git a/DFAssist.sln b/DFAssist.sln index 117f32a..546ac82 100644 --- a/DFAssist.sln +++ b/DFAssist.sln @@ -4,9 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 16.0.29424.173 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DFAssist.Plugin", "DFAssist\DFAssist.Plugin.csproj", "{1D6BA59A-FB85-4ED2-90E1-DEB79FD5AFFC}" - ProjectSection(ProjectDependencies) = postProject - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61} = {E20D32F4-26E0-4B7A-B2FE-590FA4867E61} - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{992679BB-39BD-4505-B886-AAC645B19CF0}" ProjectSection(SolutionItems) = preProject @@ -18,8 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DFAssist.Core", "DFAssist.Core\DFAssist.Core.csproj", "{67CE05FE-A32C-4149-9884-7DDC065690F9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DFAssist.WinToast", "DFAssist.WinToast\DFAssist.WinToast.vcxproj", "{E20D32F4-26E0-4B7A-B2FE-590FA4867E61}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DFAssist.Contracts", "DFAssist.Contracts\DFAssist.Contracts.csproj", "{FD838217-AEDA-4C6A-A5D8-1E6B2D6F7F2C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DFAssist", "DFAssist.Loader\DFAssist.csproj", "{67D5CCFB-C77F-4A77-A37D-ECCE57279150}" @@ -58,16 +53,6 @@ Global {67CE05FE-A32C-4149-9884-7DDC065690F9}.Release|x64.Build.0 = Release|x64 {67CE05FE-A32C-4149-9884-7DDC065690F9}.Release|x86.ActiveCfg = Release|Any CPU {67CE05FE-A32C-4149-9884-7DDC065690F9}.Release|x86.Build.0 = Release|Any CPU - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Debug|x64.ActiveCfg = Debug|x64 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Debug|x64.Build.0 = Debug|x64 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Debug|x86.ActiveCfg = Debug|Win32 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Debug|x86.Build.0 = Debug|Win32 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Release|Any CPU.ActiveCfg = Release|Win32 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Release|x64.ActiveCfg = Release|x64 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Release|x64.Build.0 = Release|x64 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Release|x86.ActiveCfg = Release|Win32 - {E20D32F4-26E0-4B7A-B2FE-590FA4867E61}.Release|x86.Build.0 = Release|Win32 {FD838217-AEDA-4C6A-A5D8-1E6B2D6F7F2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD838217-AEDA-4C6A-A5D8-1E6B2D6F7F2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD838217-AEDA-4C6A-A5D8-1E6B2D6F7F2C}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/DFAssist/Helpers/ToastHelper.cs b/DFAssist/Helpers/ToastHelper.cs index e04002d..298ed1f 100644 --- a/DFAssist/Helpers/ToastHelper.cs +++ b/DFAssist/Helpers/ToastHelper.cs @@ -1,49 +1,19 @@ using System; using System.Drawing; -using System.IO; -using System.Reflection; using System.Windows.Forms; using Advanced_Combat_Tracker; using DFAssist.Core.Toast; +using DFAssist.Core.Toast.Base; using Splat; namespace DFAssist.Helpers { public class ToastHelper : BaseNotificationHelper { - private WinToastWrapper.ToastEventCallback _toastEventCallback; - public ToastHelper() { - // we need to force the dll folder for the DFAssist.WinToast c++ library - // should be necessary just once; - // we can also avoid any check, because at this point all the libraries should be already loaded - // and all the check should have been done in the AssemblyResolver - // ReSharper disable AssignNullToNotNullAttribute - var lpPathName = Path.Combine(Path.GetDirectoryName(Locator.Current.GetService().pluginFile.ToString()), "libs"); - if(WinToastWrapper.SetDllDirectory(lpPathName)) - Logger.Write($"UI: Toast library path: {lpPathName}", LogLevel.Debug); - // ReSharper restore AssignNullToNotNullAttribute - - _toastEventCallback = delegate (int code) - { - if(code == 0) - Logger.Write("UI: Toast Clicked", LogLevel.Debug); - else if(code == 1) - Logger.Write("UI: Toast Dismissed", LogLevel.Debug); - else if(code == 2) - Logger.Write("UI: Toast Timed out", LogLevel.Debug); - else if(code == 3) - Logger.Write("UI: Toast Hidden by application", LogLevel.Debug); - else if(code == 4) - Logger.Write("UI: Toast was not activated", LogLevel.Warn); - else if(code == 11) - Logger.Write("UI: Toast showing, waiting for interaction...", LogLevel.Debug); - else if(code > 4 && code < 11) - Logger.Write($"UI: An Error occurred, code:[{code}]", LogLevel.Error); - else - Logger.Write($"UI: Interacted with the toast, using a button, code:[{code}]", LogLevel.Debug); - }; + DesktopNotificationManagerCompat.RegisterAumidAndComServer(DFAssistPlugin.AppId); + DesktopNotificationManagerCompat.RegisterActivator(); } protected override void OnSendNotification(string title, string message, string testing) @@ -68,44 +38,22 @@ protected override void OnSendNotification(string title, string message, string traySlider.ButtonNW.Visible = false; traySlider.ButtonSW.Visible = true; traySlider.ButtonSW.Text = LocalizationRepository.GetText("ui-close-act-toast"); - traySlider.ShowTraySlider($"{message}\n{testing}", title); + traySlider.ShowTraySlider($"{message}\nCode [{testing}]", title); } else { Logger.Write("UI: Using Windows Toasts", LogLevel.Debug); try { - Logger.Write("UI: Creating new Toast...", LogLevel.Debug); - var attribution = nameof(DFAssist); + // clearing old toasts if needed + DesktopNotificationManagerCompat.History.Clear(); - if (string.IsNullOrWhiteSpace(testing)) - { - WinToastWrapper.CreateToast( - DFAssistPlugin.AppId, - DFAssistPlugin.AppId, - title, - message, - _toastEventCallback, - attribution, - true, - Duration.Long); - } - else - { - WinToastWrapper.CreateToast( - DFAssistPlugin.AppId, - DFAssistPlugin.AppId, - title, - message, - $"Code [{testing}]", - _toastEventCallback, - attribution, - Duration.Long); - } + Logger.Write("UI: Creating new Toast...", LogLevel.Debug); + ToastManager.ShowToast(title, message, testing); } catch (Exception e) { - Logger.Write(e, "UI: Unable to use DFAssist.WinToast, using built in notifier...", LogLevel.Error); + Logger.Write(e, "UI: Using built in notifier...", LogLevel.Error); var icon = new NotifyIcon { Icon = SystemIcons.WinLogo, @@ -119,12 +67,5 @@ protected override void OnSendNotification(string title, string message, string } } } - - protected override void OnSetNullOwnedObjects() - { - _toastEventCallback = null; - - base.OnSetNullOwnedObjects(); - } } }