├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── msbuild.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── _config.yml ├── assets └── images │ ├── ToastImageAndText04.png │ ├── ToastText02Phone.png │ ├── Toast_1.png │ ├── Toast_2.png │ ├── Toast_4.png │ ├── Toast_5.png │ ├── Toast_6.png │ ├── Toast_7.png │ └── Toast_8.png ├── docs ├── _config.yml └── index.md ├── example ├── console-example │ ├── README.MD │ ├── WinToast Console Example.sln │ ├── WinToast Console Example.vcxproj │ ├── WinToast Console Example.vcxproj.filters │ ├── WinToast Console Example.vcxproj.user │ ├── if_terminal_298878.png │ └── main.cpp └── qt-gui-example │ └── WinToastExample │ ├── WinToastExample.pro │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── mainwindow.ui └── src ├── wintoastlib.cpp └── wintoastlib.h /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{cpp,h}] 2 | indent_style = space 3 | indent_size = 4 4 | trim_trailing_whitespace = true 5 | charset = utf-8 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: mohabouje 2 | custom: https://paypal.me/mohabouje 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | # especially 6 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot#enabling-dependabot-version-updates-for-actions 7 | 8 | version: 2 9 | updates: 10 | - package-ecosystem: "github-actions" # See documentation for possible values 11 | directory: "/" # Location of package manifests 12 | schedule: 13 | interval: "weekly" 14 | -------------------------------------------------------------------------------- /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: Build WinToast 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Path to the solution file relative to the root of the project. 7 | SOLUTION_FILE_PATH: "example/console-example/WinToast Console Example.sln" 8 | 9 | # Configuration type to build. 10 | BUILD_CONFIGURATION: Release 11 | 12 | jobs: 13 | build: 14 | runs-on: windows-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Add MSBuild to PATH 20 | uses: microsoft/setup-msbuild@v2.0.0 21 | 22 | - name: Build 23 | working-directory: ${{env.GITHUB_WORKSPACE}} 24 | run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} "${{env.SOLUTION_FILE_PATH}}" 25 | 26 | - name: Upload `wintoast.exe` 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: wintoast 30 | path: example/console-example/x64/${{env.BUILD_CONFIGURATION}} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | *.ipch 31 | *.exp 32 | *.opensdf 33 | 34 | # Visual Studio 35 | *.VC.db 36 | *.VC.VC.opendb 37 | .vs/ 38 | Debug/ 39 | Release/ 40 | enc_temp_folder/ 41 | /example/qt-gui-example/build-WinToastExample-Desktop_Qt_5_9_2_MSVC2015_32bit-Debug 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![releases](https://img.shields.io/github/tag/mohabouje/WinToast.svg) 2 | ![issues](https://img.shields.io/github/issues/mohabouje/WinToast.svg) 3 | ![license](https://img.shields.io/github/license/mohabouje/WinToast.svg) 4 | ![GitHub contributors](https://img.shields.io/github/contributors/mohabouje/WinToast.svg) 5 | ![built](https://img.shields.io/badge/built%20with-MVSC-6f62ba.svg) 6 | ![dowloads](https://img.shields.io/github/downloads/mohabouje/WinToast/total.svg) 7 | 8 | [![GitHub forks](https://img.shields.io/github/forks/mohabouje/WinToast.svg?style=social&label=Fork)]() 9 | [![GitHub stars](https://img.shields.io/github/stars/mohabouje/WinToast.svg?style=social&label=Star)]() 10 | [![GitHub watchers](https://img.shields.io/github/watchers/mohabouje/WinToast.svg?style=social&label=Watch)]() 11 | 12 | *** 13 | 14 | WinToast 15 | =================== 16 | 17 | WinToast is a lightly library written in C++ which brings a complete integration of the modern **toast notifications** of **Windows 8** & **Windows 10**. 18 | 19 | Toast notifications allows your app to inform the users about relevant information and timely events that they should see and take action upon inside your app, such as a new instant message, a new friend request, breaking news, or a calendar event. 20 | 21 | - [WinToast](#wintoast) 22 | - [Toast Templates](#toast-templates) 23 | - [Event Handler](#event-handler) 24 | - [Expiration Time](#expiration-time) 25 | - [Additional features available on Windows 10](#additional-features-available-on-windows-10) 26 | - [Error Handling](#error-handling) 27 | - [Example of Usage](#example-of-usage) 28 | - [Installation](#installation) 29 | - [Toast configuration on Windows 10](#toast-configuration-on-windows-10) 30 | - [Projects using WinToast](#projects-using-wintoast) 31 | 32 | 33 |
34 | 35 | ## Toast Templates 36 | 37 | WinToast integrates all standard templates available in the [ToastTemplateType enumeration](https://msdn.microsoft.com/en-us/library/windows/apps/br208660.aspx). 38 | 39 | | Template | Description | Example | 40 | | :------- | ----: | :---: | 41 | | `ImageAndText01` | A large image and a single string wrapped across three lines of text. | ![enter image description here](assets/images/Toast_6.png) | 42 | | `ImageAndText02` | A large image, one string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![12](assets/images/Toast_7.png) | 43 | | `ImageAndText03` | A large image, one string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](assets/images/Toast_8.png) | 44 | | `ImageAndText04` | A large image, one string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](assets/images/ToastImageAndText04.png) | 45 | | `Text01` | Single string wrapped across three lines of text. | ![enter image description here](assets/images/Toast_1.png)| 46 | | `Text02` | One string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![enter image description here](assets/images/Toast_2.png) | 47 | | `Text03` | One string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](assets/images/Toast_4.png)| 48 | | `Text04` | One string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](assets/images/Toast_5.png) | 49 | 50 | Example of a `ImageAndText02` template: 51 | 52 | ```cpp 53 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02); 54 | templ.setTextField(L"title", WinToastTemplate::FirstLine); 55 | templ.setTextField(L"subtitle", WinToastTemplate::SecondLine); 56 | templ.setImagePath(L"C:/example.png"); 57 | ``` 58 | **Note:** The user can use the default system sound or specify a sound to play when a toast notification is displayed. Same behavior for the toast notification image, by default Windows try to use the app icon.* 59 | 60 |
61 | 62 | ## Event Handler 63 | 64 | WinToast handle different events: 65 | 66 | - **Activated**: Occurs when user activates a toast notification through a click or touch. Apps that are running subscribe to this event 67 | - **Dismissed**: Occurs when a toast notification leaves the screen, either by expiring or being explicitly dismissed by the user. 68 | * Application Hidden: The application hid the toast using ToastNotifier.hide. 69 | * User Canceled: The user dismissed the toast. 70 | * Timed Out: The toast has expired 71 | - **Failed**: Occurs when an error is caused when Windows attempts to raise a toast notification. 72 | 73 | Create your custom handler to interact with the user actions by subclassing the interface `IWinToastHandler`: 74 | 75 | ```cpp 76 | class WinToastHandlerExample : public IWinToastHandler { 77 | public: 78 | WinToastHandlerExample(); 79 | // Public interfaces 80 | void toastActivated() const override; 81 | void toastDismissed(WinToastDismissalReason state) const override; 82 | void toastFailed() const override; 83 | }; 84 | ``` 85 |
86 | 87 | ## Expiration Time 88 | 89 | Set the time after which a toast notification is no longer considered current or valid and should not be displayed. Windows attempts to raise toast notifications immediately after you call Show, so this property is rarely used. 90 | 91 | > For Windows 8.x app, this property also causes the toast notification to be removed from the 92 | > Action Center once the specified data and time is reached. 93 | 94 | **Note:** Default Windows behavior is to hide notification automatically after time set in Windows Ease of Access Settings. 95 | If you need to preserve notification in Windows Action Center for longer period of time, you have to call `WinToastTemplate::setExpiration` method. 96 | 97 |
98 | 99 | ## Additional features available on Windows 10 100 | 101 | If your system supports the new modern features (Version > Windows 8.1) available in Windows 10, you can add some interesting fields as: 102 | 103 | - **Actions**: you can add your own actions, this fact allow you to interact with user in a different way: 104 | 105 | ```cpp 106 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text02); 107 | templ.setTextField(L"Do you think this feature is cool?", WinToastTemplate::FirstLine); 108 | templ.setTextField(L"Ofc,it is!", WinToastTemplate::SecondLine); 109 | 110 | std::vector actions; 111 | actions.push_back(L"Yes"); 112 | actions.push_back(L"No"); 113 | for (auto const &action : actions) 114 | templ.addAction(action); 115 | WinToast::instance()->showToast(templ, handler) 116 | ``` 117 | 118 | !["Toast with some actions"](https://lh3.googleusercontent.com/uJE_H0aBisOZ-9GynEWgA7Hha8tHEI-i0aHrFuOFDBsPSD-IJ-qEN0Y7XY4VI5hp_5MQ9xjWbFcm) 119 | - **Attribution text**: you can add/remove the attribution text, by default is empty. Use `WinToastTemplate::setAttributionText` to modify it. 120 | - **Duration**: The amount of time the toast should display. This attribute can have one of the following values: 121 | - *System*: default system configuration. 122 | - *Short*: default system short time configuration. 123 | - *Long*: default system long time configuration. 124 | - **Audio Properties**: you can modify the different behaviors of the sound: 125 | - *Default*: plays the audio file just one time. 126 | - *Silent*: turn off the sound. 127 | - *Loop*: plays the given sound in a loop during the toast existence. 128 | 129 | > WinToast allows the modification of the default audio file. Add 130 | > the given file in to your projects resources (*must be ms-appx:// or 131 | > ms-appdata:// path*) and define it by calling: `WinToastTemplate::setAudioPath` 132 | 133 | ***By default, WinToast checks if your systems support the features, ignoring the not supported ones.*** 134 | 135 |
136 | 137 | ## Error Handling 138 | There are several reasons WinToast can fail that's why the library notifies caller about fail reason. Those are the code for each failure: 139 | 140 | | WinToastError | Error Code | Error message | 141 | |--|--|--| 142 | | `NoError` | 0x00 | No error. The process was executed correctly | 143 | | `NotInitialized` | 0x01 | The library has not been initialized | 144 | | `SystemNotSupported` | 0x02 | The OS does not support WinToast | 145 | | `ShellLinkNotCreated` | 0x03 | The library was not able to create a Shell Link for the app | 146 | | `InvalidAppUserModelID` | 0x04 | The AUMI is not a valid one | 147 | | `InvalidParameters` | 0x05 | The parameters used to configure the library are not valid normally because an invalid AUMI or App Name | 148 | | `NotDisplayed` | 0x06 | The toast was created correctly but WinToast was not able to display the toast | 149 | | `UnknownError` | 0x07 | Unknown error | 150 | 151 | A common example of usage is to check while initializing the library or showing a toast notification the possible failure code: 152 | 153 | ```cpp 154 | WinToast::WinToastError error; 155 | const bool succedded = WinToast::instance()->initialize(&error); 156 | if (!succedded) { 157 | std::wcout << L"Error, could not initialize the lib. Error number: " 158 | << error << std::endl; 159 | } 160 | ... 161 | // Configure the template 162 | ... 163 | const bool launched = WinToast::instance()->showToast(templ, handler, &error); 164 | if (!launched) { 165 | std::wcout << L"Error: Could not launch your toast notification. Error: " 166 | << error << std::endl; 167 | } 168 | ``` 169 | 170 |
171 | 172 | ## Example of Usage 173 | 174 | *For an easy usage, you can just use the available singleton instance.* 175 | 176 | First step, Import the header file wintoastlib.h to your project. You should check if your Windows Version is supported by the library. 177 | 178 | ```cpp 179 | using namespace WinToastLib; 180 | .... 181 | if (!WinToast::isCompatible()) { 182 | std::wcout << L"Error, your system in not supported!" << std::endl; 183 | } 184 | ``` 185 | 186 | Configure your [App User Model Id](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459%28v=vs.85%29.aspx), this can be done by using the existing helper: 187 | 188 | ```cpp 189 | 190 | WinToast::instance()->setAppName(L"WinToastExample"); 191 | const auto aumi = WinToast::configureAUMI(L"mohabouje", L"wintoast", L"wintoastexample", L"20161006"); 192 | WinToast::instance()->setAppUserModelId(aumi); 193 | ``` 194 | 195 | Initialize all the dependencies and check if WinToast has been initialized successfully in your system: 196 | 197 | ```cpp 198 | if (!WinToast::instance()->initialize()) { 199 | std::wcout << L"Error, could not initialize the lib!" << std::endl; 200 | } 201 | ``` 202 | 203 | Implement your own action handler by subclassing the interface `IWinToastHandler` and custom your template: 204 | 205 | ```cpp 206 | WinToastHandlerExample* handler = new WinToastHandlerExample; 207 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02); 208 | templ.setImagePath(L"C:/example.png"); 209 | templ.setTextField(L"title", WinToastTemplate::FirstLine); 210 | templ.setTextField(L"subtitle", WinToastTemplate::SecondLine); 211 | ``` 212 | 213 | Finally show the results: 214 | 215 | ```cpp 216 | 217 | if (!WinToast::instance()->showToast(templ, handler)) { 218 | std::wcout << L"Error: Could not launch your toast notification!" << std::endl; 219 | } 220 | ``` 221 |
222 | 223 | ## Installation 224 | 225 | If you are using a package manager, there is a port for [vcpkg](https://github.com/microsoft/vcpkg/). Otherwise, the easiest way is to copy the source files as external dependencies. 226 | 227 | ## Toast configuration on Windows 10 228 | 229 | Windows allows the configuration of the default behavior of a toast notification. This can be done in the *Ease of Access* configuration by modifying the *Other options* tab. 230 | 231 | The system configuration helps you to define how long you want notifications to appear for (5 seconds to 5 minutes) as turning on visual notifications for sound. 232 | 233 | ![Ease of Access configuration](https://camo.githubusercontent.com/56c8edd1a7a4a43be07ba211d9d828478fdbad39/68747470733a2f2f7777772e686f77746f6765656b2e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30332f656173655f6f665f6163636573732e706e67) 234 | 235 |
236 | 237 | ## Projects using WinToast 238 | - [Git for Windows](https://github.com/git-for-windows/git): A fork of Git containing Windows-specific patches. 239 | - [QGIS](https://github.com/qgis/QGIS): QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS) 240 | - [MEGAsync](https://github.com/meganz/MEGAsync): Easy automated syncing between your computers and your MEGA Cloud Drive 241 | - [chatterino2](https://github.com/Chatterino/chatterino2): Chat client for twitch.tv 242 | - [nheko](https://github.com/Nheko-Reborn/nheko): Desktop client for the Matrix protocol. 243 | - [EDPathFinder](https://github.com/neotron/EDPathFinder): A program that creates an optimal route that passes through two or more systems in Elite. 244 | - [AntiExploit](https://github.com/Empier/Anti-Exploit): antiexploit utility for Windows. 245 | - [Zroya](https://github.com/malja/zroya): Python extension for creating native Windows notifications.. 246 | - [PidginWinToastNotifications](https://github.com/ChristianGalla/PidginWinToastNotifications): Windows Toast Notification Plugin for Pidgin. 247 | - [Dnai-Editor](https://github.com/Nicolas-Constanty/Dnai.Editor): Visual Scripting, node editor. 248 | - [Spectral](https://gitlab.com/b0/spectral): A glossy client for Matrix, written in QtQuick Controls 2 and C++. 249 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /assets/images/ToastImageAndText04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/ToastImageAndText04.png -------------------------------------------------------------------------------- /assets/images/ToastText02Phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/ToastText02Phone.png -------------------------------------------------------------------------------- /assets/images/Toast_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_1.png -------------------------------------------------------------------------------- /assets/images/Toast_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_2.png -------------------------------------------------------------------------------- /assets/images/Toast_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_4.png -------------------------------------------------------------------------------- /assets/images/Toast_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_5.png -------------------------------------------------------------------------------- /assets/images/Toast_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_6.png -------------------------------------------------------------------------------- /assets/images/Toast_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_7.png -------------------------------------------------------------------------------- /assets/images/Toast_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/assets/images/Toast_8.png -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-leap-day -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # WinToast 2 | 3 | WinToast is a lightly library written in C++ which brings a complete integration of the modern **toast notifications** of **Windows 8** & **Windows 10**. 4 | 5 | Toast notifications allows your app to inform the users about relevant information and timely events that they should see and take action upon inside your app, such as a new instant message, a new friend request, breaking news, or a calendar event. 6 | 7 | ## Event Handler 8 | 9 | WinToast implements a common interface `IWinToastHandler` to handle events: 10 | 11 | - **Activated**: Occurs when user activates a toast notification through a click or touch. Apps that are running subscribe to this event 12 | - **Dismissed**: Occurs when a toast notification leaves the screen, either by expiring or being explicitly dismissed by the user. 13 | * Application Hidden: The application hid the toast using ToastNotifier.hide. 14 | * User Canceled: The user dismissed the toast. 15 | * Timed Out: The toast has expired 16 | - **Failed**: Occurs when an error is caused when Windows attempts to raise a toast notification. 17 | 18 | Users can creates their own custom handler to interact with the user actions. For example: 19 | 20 | ```cpp 21 | class WinToastHandlerExample : public IWinToastHandler { 22 | public: 23 | WinToastHandlerExample(); 24 | // Public interfaces 25 | void toastActivated() const override; 26 | void toastDismissed(WinToastDismissalReason state) const override; 27 | void toastFailed() const override; 28 | }; 29 | 30 | ``` 31 | ## Error Handling 32 | 33 | There are several reasons WinToast can fail that's why the library notifies the caller about any possible failure reason. Those are the code for each failure: 34 | 35 | | WinToastError | Error Code | Error message | 36 | | ----------------------- | ---------- | ------------------------------------------------------------ | 37 | | `NoError` | 0x00 | No error. The process was executed correctly | 38 | | `NotInitialized` | 0x01 | The library has not been initialized | 39 | | `SystemNotSupported` | 0x02 | The OS does not support WinToast | 40 | | `ShellLinkNotCreated` | 0x03 | The library was not able to create a Shell Link for the app | 41 | | `InvalidAppUserModelID` | 0x04 | The AUMI is not a valid one | 42 | | `InvalidParameters` | 0x05 | The parameters used to configure the library are not valid normally because an invalid AUMI or App Name | 43 | | `NotDisplayed` | 0x06 | The toast was created correctly but WinToast was not able to display the toast | 44 | | `UnknownError` | 0x07 | Unknown error | 45 | 46 | A common example of usage is to check while initializing the library or showing a toast notification the possible failure code: 47 | 48 | ```cpp 49 | WinToast::WinToastError error; 50 | const bool succedded = WinToast::instance()->initialize(&error); 51 | if (!succedded) { 52 | std::wcout << L"Error, could not initialize the lib. Error: " 53 | << WinToast::strerror(error) << std::endl; 54 | } 55 | ``` 56 | 57 | ## Properties 58 | 59 | ### Templates 60 | 61 | WinToast integrates all standard templates available in the [ToastTemplateType enumeration](https://msdn.microsoft.com/en-us/library/windows/apps/br208660.aspx). 62 | 63 | 64 | 65 | | Template | Description | Example | 66 | | :--------------- | -----------------------------------------------------------: | :----------------------------------------------------------: | 67 | | `ImageAndText01` | A large image and a single string wrapped across three lines of text. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601606.png) | 68 | | `ImageAndText02` | A large image, one string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![12](https://i-msdn.sec.s-msft.com/dynimg/IC601607.png) | 69 | | `ImageAndText03` | A large image, one string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601608.png) | 70 | | `ImageAndText04` | A large image, one string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601609.png) | 71 | | `Text01` | Single string wrapped across three lines of text. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601602.png) | 72 | | `Text02` | One string of bold text on the first line, one string of regular text wrapped across the second and third lines. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601603.png) | 73 | | `Text03` | One string of bold text wrapped across the first two lines, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601604.png) | 74 | | `Text04` | One string of bold text on the first line, one string of regular text on the second line, one string of regular text on the third line. | ![enter image description here](https://i-msdn.sec.s-msft.com/dynimg/IC601605.png) | 75 | 76 | Example of a `ImageAndText02` template: 77 | 78 | ```cpp 79 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02); 80 | templ.setTextField(L"title", WinToastTemplate::FirstLine); 81 | templ.setTextField(L"subtitle", WinToastTemplate::SecondLine); 82 | templ.setImagePath(L"C:/example.png"); 83 | ``` 84 | 85 | **Note:** The user can use the default system sound or specify a sound to play when a toast notification is displayed. Same behavior for the toast notification image, by default Windows try to use the app icon. 86 | 87 | ### Expiration Time 88 | 89 | This property sets the time after which a toast notification is no longer considered current or valid and should not be displayed. Windows attempts to raise toast notifications immediately after you call Show, so this property is rarely used. 90 | 91 | > For Windows 8.x app, this property also causes the toast notification to be removed from the 92 | > Action Center once the specified data and time is reached. 93 | 94 | > Not: Default Windows behavior is to hide notification automatically after time set in Windows Ease of Access Settings. 95 | 96 | If you need to preserve notification in Windows Action Center for longer period of time, you have to call `WinToastTemplate::setExpiration` method. 97 | 98 | ### Actions 99 | 100 | WinToast provides an easy interface to add actions (buttons) to a toast notification. This feature allows the interaction with user in a different way: 101 | 102 | ```cpp 103 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text02); 104 | templ.setTextField(L"Do you think this feature is cool?", WinToastTemplate::FirstLine); 105 | templ.setTextField(L"Ofc,it is!", WinToastTemplate::SecondLine); 106 | 107 | std::vector actions; 108 | actions.push_back(L"Yes"); 109 | actions.push_back(L"No"); 110 | for (auto const &action : actions) 111 | templ.addAction(action); 112 | WinToast::instance()->showToast(templ, handler) 113 | ``` 114 | 115 | This code will display something like this: 116 | 117 | !["Toast with some actions"](https://lh3.googleusercontent.com/uJE_H0aBisOZ-9GynEWgA7Hha8tHEI-i0aHrFuOFDBsPSD-IJ-qEN0Y7XY4VI5hp_5MQ9xjWbFcm) 118 | 119 | ### Atribution Text 120 | 121 | In the latest versions of Windows, users can add/remove an attribution text (empty by default). WinToast integrates a simple interface to change this property `WinToastTemplate::setAttributionText`: 122 | 123 | ```c++ 124 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text01); 125 | templ.setTextField(L"Do you think this feature is cool?", WinToastTemplate::FirstLine); 126 | templ.setAttributionText(L"This is an attribution text"); 127 | WinToast::instance()->showToast(templ, handler) 128 | ``` 129 | 130 | ### Duration 131 | 132 | Users can change the amount of time the toast should be displayed. This attribute can have one of the following values: 133 | 134 | - *System*: default system configuration. 135 | - *Short*: default system short time configuration. 136 | - *Long*: default system long time configuration. 137 | 138 | ```c++ 139 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text01); 140 | templ.setFirstLine(L"I will be displayed for a long time"); 141 | templ.setDuration(WinToastTemplate::Duration::Long); 142 | WinToast::instance()->showToast(templ, handler) 143 | ``` 144 | 145 | ### Audio 146 | 147 | Users can modify the different behaviors of the sound. For instance, users can specify the default play mode: 148 | 149 | - *Default*: plays the audio file just one time. 150 | - *Silent*: turn off the sound. 151 | - *Loop*: plays the given sound in a loop during the toast existence. 152 | 153 | > WinToast allows the modification of the default audio file. There are different audio files installed by default in the system that can be used via the `WinToastTemplate::AudioSystemFile` enumeration. See more details in this [link](https://docs.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-audio). 154 | > 155 | > For instance, to display an alarm that will play the same sound in a loop we could do something like this: 156 | > 157 | > ```c++ 158 | > WinToastTemplate templ = WinToastTemplate(WinToastTemplate::Text01); 159 | > templ.setFirstLine(L"I am an alarm"); 160 | > templ.setDuration(WinToastTemplate::Duration::Long); 161 | > templ.setAudioMode(WinToastTemplate::AudioOption::Loop); 162 | > templ.setAudioPath(WinToastTemplate::AudioSystemFile::Alarm); 163 | > WinToast::instance()->showToast(templ, handler) 164 | > ``` 165 | 166 | ## Toast configuration on Windows 10 167 | 168 | Windows allows the configuration of the default behavior of a toast notification. This can be done in the *Ease of Access* configuration by modifying the *Other options* tab. 169 | 170 | The system configuration helps you to define how long you want notifications to appear for (5 seconds to 5 minutes) as turning on visual notifications for sound. 171 | 172 | ![Ease of Access configuration](https://camo.githubusercontent.com/56c8edd1a7a4a43be07ba211d9d828478fdbad39/68747470733a2f2f7777772e686f77746f6765656b2e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30332f656173655f6f665f6163636573732e706e67) 173 | 174 |
175 | 176 | ## Projects using WinToast 177 | - [Git for Windows](https://github.com/git-for-windows/git): A fork of Git containing Windows-specific patches. 178 | - [QGIS](https://github.com/qgis/QGIS): QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS) 179 | - [nheko](https://github.com/mujx/nheko): Desktop client for the Matrix protocol. 180 | - [EDPathFinder](https://github.com/neotron/EDPathFinder): A program that creates an optimal route that passes through two or more systems in Elite. 181 | - [AntiExploit](https://github.com/Empier/Anti-Exploit): antiexploit utility for Windows. 182 | - [Zroya](https://github.com/malja/zroya): Python extension for creating native Windows notifications.. 183 | - [PidginWinToastNotifications](https://github.com/ChristianGalla/PidginWinToastNotifications): Windows Toast Notification Plugin for Pidgin. 184 | - [Dnai-Editor](https://github.com/Nicolas-Constanty/Dnai.Editor): Visual Scripting, node editor. 185 | - [Spectral](https://gitlab.com/b0/spectral): A glossy client for Matrix, written in QtQuick Controls 2 and C++. 186 | -------------------------------------------------------------------------------- /example/console-example/README.MD: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinToast Console Example! 4 | =================== 5 | 6 | WinToast Contole Example [OPTIONS] 7 | --action : Set the actions in buttons 8 | --aumi : Set the App User Model Id 9 | --appname : Set the default appname 10 | --appid : Set the App Id 11 | --expirems : Set the default expiration time 12 | --text : Set the text for the notifications 13 | --image : set the image path 14 | --help : Print the help description 15 | 16 | Example: 17 | 18 | 19 | "WinToast Contole Example.exe" --appname "Yolo" --aumi "My.Console.Example" --image "if_terminal_298878.png" --expirems 10000 --action Yes --action No --text "Do you want to try to take over the world?" 20 | 21 | -------------------------------------------------------------------------------- /example/console-example/WinToast Console Example.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinToast Console Example", "WinToast Console Example.vcxproj", "{7C5AC60D-8668-47B6-9D05-FB363F854065}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Debug|ARM64 = Debug|ARM64 13 | Release|x64 = Release|x64 14 | Release|x86 = Release|x86 15 | Release|ARM64 = Release|ARM64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.ActiveCfg = Debug|Win32 19 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.Build.0 = Debug|Win32 20 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x86.ActiveCfg = Debug|Win32 21 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x86.Build.0 = Debug|Win32 22 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|ARM64.ActiveCfg = Debug|ARM64 23 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|ARM64.Build.0 = Debug|ARM64 24 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|x64.ActiveCfg = Release|x64 25 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|x64.Build.0 = Release|x64 26 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|x86.ActiveCfg = Release|Win32 27 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|x86.Build.0 = Release|Win32 28 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|ARM64.ActiveCfg = Release|ARM64 29 | {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|ARM64.Build.0 = Release|ARM64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {ABA98BF8-B665-455B-A51E-B36DD616139C} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /example/console-example/WinToast Console Example.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM64 23 | 24 | 25 | Release 26 | ARM64 27 | 28 | 29 | 30 | 15.0 31 | {7C5AC60D-8668-47B6-9D05-FB363F854065} 32 | Win32Proj 33 | 10.0 34 | 35 | 36 | 37 | Application 38 | true 39 | v143 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | true 52 | 53 | 54 | 55 | WIN32;_CONSOLE;%(PreprocessorDefinitions) 56 | Level3 57 | ProgramDatabase 58 | 59 | 60 | true 61 | Console 62 | 63 | 64 | 65 | 66 | MachineX86 67 | 68 | 69 | 70 | 71 | MachineX64 72 | 73 | 74 | 75 | 76 | NDEBUG;%(PreprocessorDefinitions) 77 | MultiThreaded 78 | MinSpace 79 | Default 80 | 81 | 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | _DEBUG;%(PreprocessorDefinitions) 89 | Disabled 90 | MultiThreadedDebug 91 | 92 | 93 | 94 | 95 | 96 | ..\..\src;%(AdditionalIncludeDirectories) 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /example/console-example/WinToast Console Example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | 31 | 32 | Resource Files 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/console-example/WinToast Console Example.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --appname "Yolo" --aumi "My.Console.Example" --image "$(ProjectDir)if_terminal_298878.png" --expirems 10000 --action Yes --action No --text "Do you want to try to take over the world?" 5 | WindowsLocalDebugger 6 | 7 | -------------------------------------------------------------------------------- /example/console-example/if_terminal_298878.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/git-for-windows/WinToast/b2011bb25efce043f583c6bae5d2629d00744316/example/console-example/if_terminal_298878.png -------------------------------------------------------------------------------- /example/console-example/main.cpp: -------------------------------------------------------------------------------- 1 | #include "wintoastlib.h" 2 | #include // std::string, std::stoi 3 | 4 | using namespace WinToastLib; 5 | 6 | class CustomHandler : public IWinToastHandler { 7 | public: 8 | void toastActivated() const { 9 | std::wcout << L"The user clicked in this toast" << std::endl; 10 | exit(0); 11 | } 12 | 13 | void toastActivated(int actionIndex) const { 14 | std::wcout << L"The user clicked on action #" << actionIndex << std::endl; 15 | exit(16 + actionIndex); 16 | } 17 | 18 | void toastDismissed(WinToastDismissalReason state) const { 19 | switch (state) { 20 | case UserCanceled: 21 | std::wcout << L"The user dismissed this toast" << std::endl; 22 | exit(1); 23 | break; 24 | case TimedOut: 25 | std::wcout << L"The toast has timed out" << std::endl; 26 | exit(2); 27 | break; 28 | case ApplicationHidden: 29 | std::wcout << L"The application hid the toast using ToastNotifier.hide()" << std::endl; 30 | exit(3); 31 | break; 32 | default: 33 | std::wcout << L"Toast not activated" << std::endl; 34 | exit(4); 35 | break; 36 | } 37 | } 38 | 39 | void toastFailed() const { 40 | std::wcout << L"Error showing current toast" << std::endl; 41 | exit(5); 42 | } 43 | }; 44 | 45 | 46 | enum Results { 47 | ToastClicked, // user clicked on the toast 48 | ToastDismissed, // user dismissed the toast 49 | ToastTimeOut, // toast timed out 50 | ToastHided, // application hid the toast 51 | ToastNotActivated, // toast was not activated 52 | ToastFailed, // toast failed 53 | SystemNotSupported, // system does not support toasts 54 | UnhandledOption, // unhandled option 55 | MultipleTextNotSupported, // multiple texts were provided 56 | InitializationFailure, // toast notification manager initialization failure 57 | ToastNotLaunched // toast could not be launched 58 | }; 59 | 60 | 61 | #define COMMAND_ACTION L"--action" 62 | #define COMMAND_AUMI L"--aumi" 63 | #define COMMAND_APPNAME L"--appname" 64 | #define COMMAND_APPID L"--appid" 65 | #define COMMAND_EXPIREMS L"--expirems" 66 | #define COMMAND_TEXT L"--text" 67 | #define COMMAND_HELP L"--help" 68 | #define COMMAND_IMAGE L"--image" 69 | #define COMMAND_SHORTCUT L"--only-create-shortcut" 70 | #define COMMAND_AUDIOSTATE L"--audio-state" 71 | #define COMMAND_ATTRIBUTE L"--attribute" 72 | 73 | void print_help() { 74 | std::wcout << "WinToast Console Example [OPTIONS]" << std::endl; 75 | std::wcout << "\t" << COMMAND_ACTION << L" : Set the actions in buttons" << std::endl; 76 | std::wcout << "\t" << COMMAND_AUMI << L" : Set the App User Model Id" << std::endl; 77 | std::wcout << "\t" << COMMAND_APPNAME << L" : Set the default appname" << std::endl; 78 | std::wcout << "\t" << COMMAND_APPID << L" : Set the App Id" << std::endl; 79 | std::wcout << "\t" << COMMAND_EXPIREMS << L" : Set the default expiration time" << std::endl; 80 | std::wcout << "\t" << COMMAND_TEXT << L" : Set the text for the notifications" << std::endl; 81 | std::wcout << "\t" << COMMAND_IMAGE << L" : set the image path" << std::endl; 82 | std::wcout << "\t" << COMMAND_ATTRIBUTE << L" : set the attribute for the notification" << std::endl; 83 | std::wcout << "\t" << COMMAND_SHORTCUT << L" : create the shortcut for the app" << std::endl; 84 | std::wcout << "\t" << COMMAND_AUDIOSTATE << L" : set the audio state: Default = 0, Silent = 1, Loop = 2" << std::endl; 85 | std::wcout << "\t" << COMMAND_HELP << L" : Print the help description" << std::endl; 86 | } 87 | 88 | 89 | int wmain(int argc, LPWSTR *argv) 90 | { 91 | if (argc == 1) { 92 | print_help(); 93 | return 0; 94 | } 95 | 96 | if (!WinToast::isCompatible()) { 97 | std::wcerr << L"Error, your system in not supported!" << std::endl; 98 | return Results::SystemNotSupported; 99 | } 100 | 101 | LPWSTR appName = L"Console WinToast Example", 102 | appUserModelID = L"WinToast Console Example", 103 | text = NULL, 104 | imagePath = NULL, 105 | attribute = L"default"; 106 | std::vector actions; 107 | INT64 expiration = 0; 108 | 109 | 110 | bool onlyCreateShortcut = false; 111 | WinToastTemplate::AudioOption audioOption = WinToastTemplate::AudioOption::Default; 112 | 113 | int i; 114 | for (i = 1; i < argc; i++) 115 | if (!wcscmp(COMMAND_IMAGE, argv[i])) 116 | imagePath = argv[++i]; 117 | else if (!wcscmp(COMMAND_ACTION, argv[i])) 118 | actions.push_back(argv[++i]); 119 | else if (!wcscmp(COMMAND_EXPIREMS, argv[i])) 120 | expiration = wcstol(argv[++i], NULL, 10); 121 | else if (!wcscmp(COMMAND_APPNAME, argv[i])) 122 | appName = argv[++i]; 123 | else if (!wcscmp(COMMAND_AUMI, argv[i]) || !wcscmp(COMMAND_APPID, argv[i])) 124 | appUserModelID = argv[++i]; 125 | else if (!wcscmp(COMMAND_TEXT, argv[i])) 126 | text = argv[++i]; 127 | else if (!wcscmp(COMMAND_ATTRIBUTE, argv[i])) 128 | attribute = argv[++i]; 129 | else if (!wcscmp(COMMAND_SHORTCUT, argv[i])) 130 | onlyCreateShortcut = true; 131 | else if (!wcscmp(COMMAND_AUDIOSTATE, argv[i])) 132 | audioOption = static_cast(std::stoi(argv[++i])); 133 | else if (!wcscmp(COMMAND_HELP, argv[i])) { 134 | print_help(); 135 | return 0; 136 | } else { 137 | std::wcerr << L"Option not recognized: " << argv[i] << std::endl; 138 | return Results::UnhandledOption; 139 | } 140 | 141 | WinToast::instance()->setAppName(appName); 142 | WinToast::instance()->setAppUserModelId(appUserModelID); 143 | 144 | if (onlyCreateShortcut) { 145 | if (imagePath || text || actions.size() > 0 || expiration) { 146 | std::wcerr << L"--only-create-shortcut does not accept images/text/actions/expiration" << std::endl; 147 | return 9; 148 | } 149 | enum WinToast::ShortcutResult result = WinToast::instance()->createShortcut(); 150 | return result ? 16 + result : 0; 151 | } 152 | 153 | if (!text) 154 | text = L"Hello, world!"; 155 | 156 | if (!WinToast::instance()->initialize()) { 157 | std::wcerr << L"Error, your system in not compatible!" << std::endl; 158 | return Results::InitializationFailure; 159 | } 160 | 161 | bool withImage = (imagePath != NULL); 162 | WinToastTemplate templ( withImage ? WinToastTemplate::ImageAndText02 : WinToastTemplate::Text02); 163 | templ.setTextField(text, WinToastTemplate::FirstLine); 164 | templ.setAudioOption(audioOption); 165 | templ.setAttributionText(attribute); 166 | 167 | for (auto const &action : actions) 168 | templ.addAction(action); 169 | if (expiration) 170 | templ.setExpiration(expiration); 171 | if (withImage) 172 | templ.setImagePath(imagePath); 173 | 174 | 175 | if (WinToast::instance()->showToast(templ, new CustomHandler()) < 0) { 176 | std::wcerr << L"Could not launch your toast notification!"; 177 | return Results::ToastFailed; 178 | } 179 | 180 | // Give the handler a chance for 15 seconds (or the expiration plus 1 second) 181 | Sleep(expiration ? (DWORD)expiration + 1000 : 15000); 182 | 183 | exit(2); 184 | } 185 | -------------------------------------------------------------------------------- /example/qt-gui-example/WinToastExample/WinToastExample.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2016-10-01T17:29:36 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | CONFIG += c++11 9 | 10 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 11 | 12 | TARGET = WinToastExample 13 | TEMPLATE = app 14 | 15 | 16 | SOURCES += main.cpp\ 17 | mainwindow.cpp \ 18 | ../../../src/wintoastlib.cpp 19 | 20 | HEADERS += mainwindow.h \ 21 | ../../../src/wintoastlib.h 22 | 23 | FORMS += mainwindow.ui 24 | 25 | #win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../lib/Release/ -lWinToastLib 26 | #else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../lib/Debug/ -lWinToastLib 27 | 28 | -------------------------------------------------------------------------------- /example/qt-gui-example/WinToastExample/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /example/qt-gui-example/WinToastExample/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../../../src/wintoastlib.h" 8 | using namespace WinToastLib; 9 | MainWindow::MainWindow(QWidget *parent) : 10 | QMainWindow(parent), 11 | ui(new Ui::MainWindow) 12 | { 13 | ui->setupUi(this); 14 | 15 | ui->audioMode->addItem("Default", WinToastTemplate::AudioOption::Default); 16 | ui->audioMode->addItem("Loop", WinToastTemplate::AudioOption::Loop); 17 | ui->audioMode->addItem("Silence", WinToastTemplate::AudioOption::Silent); 18 | 19 | ui->audioSystemFile->addItem("Default", WinToastTemplate::AudioSystemFile::DefaultSound); 20 | ui->audioSystemFile->addItem("Mail", WinToastTemplate::AudioSystemFile::Mail); 21 | ui->audioSystemFile->addItem("SMS", WinToastTemplate::AudioSystemFile::SMS); 22 | ui->audioSystemFile->addItem("Alarm", WinToastTemplate::AudioSystemFile::Alarm); 23 | 24 | WinToast::instance()->setAppName(L"WinToastExample"); 25 | WinToast::instance()->setAppUserModelId( 26 | WinToast::configureAUMI(L"mohabouje", L"wintoast", L"wintoastexample", L"20161006")); 27 | if (!WinToast::instance()->initialize()) { 28 | qDebug() << "Error, your system in not compatible!"; 29 | } 30 | } 31 | 32 | MainWindow::~MainWindow() 33 | { 34 | delete ui; 35 | } 36 | 37 | void MainWindow::on_imagePathSelector_clicked() 38 | { 39 | const QString fileName = QFileDialog::getOpenFileName(this, "Select an image", QDir::currentPath(), "*.png"); 40 | if (fileName.isEmpty()) 41 | return; 42 | ui->imagePath->setText(QDir::toNativeSeparators(fileName)); 43 | 44 | } 45 | 46 | class CustomHandler : public IWinToastHandler { 47 | public: 48 | void toastActivated() const { 49 | std::wcout << L"The user clicked in this toast" << std::endl; 50 | } 51 | 52 | void toastActivated(int actionIndex) const { 53 | std::wcout << L"The user clicked on button #" << actionIndex << L" in this toast" << std::endl; 54 | } 55 | 56 | void toastFailed() const { 57 | std::wcout << L"Error showing current toast" << std::endl; 58 | } 59 | void toastDismissed(WinToastDismissalReason state) const { 60 | switch (state) { 61 | case UserCanceled: 62 | std::wcout << L"The user dismissed this toast" << std::endl; 63 | break; 64 | case ApplicationHidden: 65 | std::wcout << L"The application hid the toast using ToastNotifier.hide()" << std::endl; 66 | break; 67 | case TimedOut: 68 | std::wcout << L"The toast has timed out" << std::endl; 69 | break; 70 | default: 71 | std::wcout << L"Toast not activated" << std::endl; 72 | break; 73 | } 74 | } 75 | }; 76 | 77 | void MainWindow::on_showToast_clicked() 78 | { 79 | WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText04); 80 | templ.setImagePath(ui->imagePath->text().toStdWString()); 81 | templ.setTextField(ui->firstLine->text().toStdWString(), WinToastTemplate::FirstLine); 82 | templ.setTextField(ui->secondLine->text().toStdWString(), WinToastTemplate::SecondLine); 83 | templ.setTextField(ui->secondLine->text().toStdWString(), WinToastTemplate::ThirdLine); 84 | templ.setExpiration(ui->spinBox->value() * 1000); 85 | templ.setAudioPath(static_cast(ui->audioSystemFile->currentData().toInt())); 86 | templ.setAudioOption(static_cast(ui->audioMode->currentData().toInt())); 87 | if (ui->addYes->isChecked()) templ.addAction(L"Yes"); 88 | if (ui->addNo->isChecked()) templ.addAction(L"No"); 89 | 90 | 91 | if (WinToast::instance()->showToast(templ, new CustomHandler()) < 0) { 92 | QMessageBox::warning(this, "Error", "Could not launch your toast notification!"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /example/qt-gui-example/WinToastExample/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class MainWindow; 8 | } 9 | 10 | class MainWindow : public QMainWindow 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | private slots: 19 | void on_imagePathSelector_clicked(); 20 | 21 | void on_showToast_clicked(); 22 | 23 | private: 24 | Ui::MainWindow *ui; 25 | }; 26 | 27 | #endif // MAINWINDOW_H 28 | -------------------------------------------------------------------------------- /example/qt-gui-example/WinToastExample/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 670 10 | 220 11 | 12 | 13 | 14 | WinToast Example 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | First Line 25 | 26 | 27 | 28 | 29 | 30 | 31 | Second Line 32 | 33 | 34 | 35 | 36 | 37 | 38 | Add Yes Action 39 | 40 | 41 | 42 | 43 | 44 | 45 | Audio System File 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 0 54 | 55 | 56 | 0 57 | 58 | 59 | 0 60 | 61 | 62 | 0 63 | 64 | 65 | 66 | 67 | Qt::Horizontal 68 | 69 | 70 | 71 | 40 72 | 20 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 0 82 | 0 83 | 84 | 85 | 86 | Show Toast 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | Image Path 97 | 98 | 99 | 100 | 101 | 102 | 103 | Add No Action 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Expiration time (secs) 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 10 124 | 125 | 126 | 2 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 0 135 | 0 136 | 137 | 138 | 139 | 140 | 0 141 | 142 | 143 | 0 144 | 145 | 146 | 0 147 | 148 | 149 | 0 150 | 151 | 152 | 153 | 154 | 155 | 0 156 | 0 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | ... 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | Audio Mode 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/wintoastlib.cpp: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2016-2019 Mohammed Boujemaoui 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #include "wintoastlib.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #pragma comment(lib,"shlwapi") 28 | #pragma comment(lib,"user32") 29 | 30 | #ifdef NDEBUG 31 | #define DEBUG_MSG(str) do { } while ( false ) 32 | #else 33 | #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false ) 34 | #endif 35 | 36 | #define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" 37 | #define DEFAULT_LINK_FORMAT L".lnk" 38 | #define STATUS_SUCCESS (0x00000000) 39 | 40 | 41 | // Quickstart: Handling toast activations from Win32 apps in Windows 10 42 | // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ 43 | using namespace WinToastLib; 44 | namespace DllImporter { 45 | 46 | // Function load a function from library 47 | template 48 | HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { 49 | if (!library) { 50 | return E_INVALIDARG; 51 | } 52 | func = reinterpret_cast(GetProcAddress(library, name)); 53 | return (func != nullptr) ? S_OK : E_FAIL; 54 | } 55 | 56 | typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); 57 | typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); 58 | typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); 59 | 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); 60 | typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32 *length); 61 | typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); 62 | 63 | static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; 64 | static f_PropVariantToString PropVariantToString; 65 | static f_RoGetActivationFactory RoGetActivationFactory; 66 | static f_WindowsCreateStringReference WindowsCreateStringReference; 67 | static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; 68 | static f_WindowsDeleteString WindowsDeleteString; 69 | 70 | 71 | template 72 | _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { 73 | return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); 74 | } 75 | 76 | template 77 | inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { 78 | return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); 79 | } 80 | 81 | inline HRESULT initialize() { 82 | HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); 83 | HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); 84 | if (SUCCEEDED(hr)) { 85 | HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); 86 | hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); 87 | if (SUCCEEDED(hr)) { 88 | HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); 89 | const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) 90 | && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) 91 | && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) 92 | && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); 93 | return succeded ? S_OK : E_FAIL; 94 | } 95 | } 96 | return hr; 97 | } 98 | } 99 | 100 | class WinToastStringWrapper { 101 | public: 102 | WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept { 103 | HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); 104 | if (!SUCCEEDED(hr)) { 105 | RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); 106 | } 107 | } 108 | 109 | WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept { 110 | HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); 111 | if (FAILED(hr)) { 112 | RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); 113 | } 114 | } 115 | 116 | ~WinToastStringWrapper() { 117 | DllImporter::WindowsDeleteString(_hstring); 118 | } 119 | 120 | inline HSTRING Get() const noexcept { 121 | return _hstring; 122 | } 123 | private: 124 | HSTRING _hstring; 125 | HSTRING_HEADER _header; 126 | 127 | }; 128 | 129 | class InternalDateTime : public IReference { 130 | public: 131 | static INT64 Now() { 132 | FILETIME now; 133 | GetSystemTimeAsFileTime(&now); 134 | return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); 135 | } 136 | 137 | InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} 138 | 139 | InternalDateTime(INT64 millisecondsFromNow) { 140 | _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; 141 | } 142 | 143 | virtual ~InternalDateTime() = default; 144 | 145 | operator INT64() { 146 | return _dateTime.UniversalTime; 147 | } 148 | 149 | HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) { 150 | *dateTime = _dateTime; 151 | return S_OK; 152 | } 153 | 154 | HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) { 155 | if (!ppvObject) { 156 | return E_POINTER; 157 | } 158 | if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference)) { 159 | *ppvObject = static_cast(static_cast*>(this)); 160 | return S_OK; 161 | } 162 | return E_NOINTERFACE; 163 | } 164 | 165 | ULONG STDMETHODCALLTYPE Release() { 166 | return 1; 167 | } 168 | 169 | ULONG STDMETHODCALLTYPE AddRef() { 170 | return 2; 171 | } 172 | 173 | HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) { 174 | return E_NOTIMPL; 175 | } 176 | 177 | HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) { 178 | return E_NOTIMPL; 179 | } 180 | 181 | HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) { 182 | return E_NOTIMPL; 183 | } 184 | 185 | protected: 186 | DateTime _dateTime; 187 | }; 188 | 189 | namespace Util { 190 | 191 | typedef LONG NTSTATUS, *PNTSTATUS; 192 | typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); 193 | inline RTL_OSVERSIONINFOW getRealOSVersion() { 194 | HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); 195 | if (hMod) { 196 | RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); 197 | if (fxPtr != nullptr) { 198 | RTL_OSVERSIONINFOW rovi = { 0 }; 199 | rovi.dwOSVersionInfoSize = sizeof(rovi); 200 | if (STATUS_SUCCESS == fxPtr(&rovi)) { 201 | return rovi; 202 | } 203 | } 204 | } 205 | RTL_OSVERSIONINFOW rovi = { 0 }; 206 | return rovi; 207 | } 208 | 209 | inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { 210 | DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); 211 | DEBUG_MSG("Default executable path: " << path); 212 | return (written > 0) ? S_OK : E_FAIL; 213 | } 214 | 215 | 216 | inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { 217 | DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); 218 | HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; 219 | if (SUCCEEDED(hr)) { 220 | errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); 221 | hr = (result == 0) ? S_OK : E_INVALIDARG; 222 | DEBUG_MSG("Default shell link path: " << path); 223 | } 224 | return hr; 225 | } 226 | 227 | inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { 228 | HRESULT hr = defaultShellLinksDirectory(path, nSize); 229 | if (SUCCEEDED(hr)) { 230 | const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); 231 | errno_t result = wcscat_s(path, nSize, appLink.c_str()); 232 | hr = (result == 0) ? S_OK : E_INVALIDARG; 233 | DEBUG_MSG("Default shell link file path: " << path); 234 | } 235 | return hr; 236 | } 237 | 238 | 239 | inline PCWSTR AsString(ComPtr &xmlDocument) { 240 | HSTRING xml; 241 | ComPtr ser; 242 | HRESULT hr = xmlDocument.As(&ser); 243 | hr = ser->GetXml(&xml); 244 | if (SUCCEEDED(hr)) 245 | return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); 246 | return nullptr; 247 | } 248 | 249 | inline PCWSTR AsString(HSTRING hstring) { 250 | return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); 251 | } 252 | 253 | inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) { 254 | ComPtr textNode; 255 | HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); 256 | if (SUCCEEDED(hr)) { 257 | ComPtr stringNode; 258 | hr = textNode.As(&stringNode); 259 | if (SUCCEEDED(hr)) { 260 | ComPtr appendedChild; 261 | hr = node->AppendChild(stringNode.Get(), &appendedChild); 262 | } 263 | } 264 | return hr; 265 | } 266 | 267 | inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime) { 268 | EventRegistrationToken activatedToken, dismissedToken, failedToken; 269 | HRESULT hr = notification->add_Activated( 270 | Callback < Implements < RuntimeClassFlags, 271 | ITypedEventHandler> >( 272 | [eventHandler](IToastNotification*, IInspectable* inspectable) 273 | { 274 | IToastActivatedEventArgs *activatedEventArgs; 275 | HRESULT hr = inspectable->QueryInterface(&activatedEventArgs); 276 | if (SUCCEEDED(hr)) { 277 | HSTRING argumentsHandle; 278 | hr = activatedEventArgs->get_Arguments(&argumentsHandle); 279 | if (SUCCEEDED(hr)) { 280 | PCWSTR arguments = Util::AsString(argumentsHandle); 281 | if (arguments && *arguments) { 282 | eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); 283 | return S_OK; 284 | } 285 | } 286 | } 287 | eventHandler->toastActivated(); 288 | return S_OK; 289 | }).Get(), &activatedToken); 290 | 291 | if (SUCCEEDED(hr)) { 292 | hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, 293 | ITypedEventHandler> >( 294 | [eventHandler, expirationTime](IToastNotification*, IToastDismissedEventArgs* e) 295 | { 296 | ToastDismissalReason reason; 297 | if (SUCCEEDED(e->get_Reason(&reason))) 298 | { 299 | if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime) 300 | reason = ToastDismissalReason_TimedOut; 301 | eventHandler->toastDismissed(static_cast(reason)); 302 | } 303 | return S_OK; 304 | }).Get(), &dismissedToken); 305 | if (SUCCEEDED(hr)) { 306 | hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, 307 | ITypedEventHandler> >( 308 | [eventHandler](IToastNotification*, IToastFailedEventArgs*) 309 | { 310 | eventHandler->toastFailed(); 311 | return S_OK; 312 | }).Get(), &failedToken); 313 | } 314 | } 315 | return hr; 316 | } 317 | 318 | inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) { 319 | ComPtr srcAttribute; 320 | HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); 321 | if (SUCCEEDED(hr)) { 322 | ComPtr node; 323 | hr = srcAttribute.As(&node); 324 | if (SUCCEEDED(hr)) { 325 | ComPtr pNode; 326 | hr = attributeMap->SetNamedItem(node.Get(), &pNode); 327 | } 328 | } 329 | return hr; 330 | } 331 | 332 | inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector& attribute_names) { 333 | ComPtr rootList; 334 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); 335 | if (SUCCEEDED(hr)) { 336 | ComPtr root; 337 | hr = rootList->Item(0, &root); 338 | if (SUCCEEDED(hr)) { 339 | ComPtr audioElement; 340 | hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); 341 | if (SUCCEEDED(hr)) { 342 | ComPtr audioNodeTmp; 343 | hr = audioElement.As(&audioNodeTmp); 344 | if (SUCCEEDED(hr)) { 345 | ComPtr audioNode; 346 | hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); 347 | if (SUCCEEDED(hr)) { 348 | ComPtr attributes; 349 | hr = audioNode->get_Attributes(&attributes); 350 | if (SUCCEEDED(hr)) { 351 | for (const auto& it : attribute_names) { 352 | hr = addAttribute(xml, it, attributes.Get()); 353 | } 354 | } 355 | } 356 | } 357 | } 358 | } 359 | } 360 | return hr; 361 | } 362 | } 363 | 364 | WinToast* WinToast::instance() { 365 | static WinToast instance; 366 | return &instance; 367 | } 368 | 369 | WinToast::WinToast() : 370 | _isInitialized(false), 371 | _hasCoInitialized(false) 372 | { 373 | if (!isCompatible()) { 374 | DEBUG_MSG(L"Warning: Your system is not compatible with this library "); 375 | } 376 | } 377 | 378 | WinToast::~WinToast() { 379 | if (_hasCoInitialized) { 380 | CoUninitialize(); 381 | } 382 | } 383 | 384 | void WinToast::setAppName(_In_ const std::wstring& appName) { 385 | _appName = appName; 386 | } 387 | 388 | 389 | void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { 390 | _aumi = aumi; 391 | DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); 392 | } 393 | 394 | void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { 395 | _shortcutPolicy = shortcutPolicy; 396 | } 397 | 398 | bool WinToast::isCompatible() { 399 | DllImporter::initialize(); 400 | return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) 401 | || (DllImporter::PropVariantToString == nullptr) 402 | || (DllImporter::RoGetActivationFactory == nullptr) 403 | || (DllImporter::WindowsCreateStringReference == nullptr) 404 | || (DllImporter::WindowsDeleteString == nullptr)); 405 | } 406 | 407 | bool WinToastLib::WinToast::isSupportingModernFeatures() { 408 | constexpr auto MinimumSupportedVersion = 6; 409 | return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; 410 | 411 | } 412 | std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, 413 | _In_ const std::wstring &productName, 414 | _In_ const std::wstring &subProduct, 415 | _In_ const std::wstring &versionInformation) 416 | { 417 | std::wstring aumi = companyName; 418 | aumi += L"." + productName; 419 | if (subProduct.length() > 0) { 420 | aumi += L"." + subProduct; 421 | if (versionInformation.length() > 0) { 422 | aumi += L"." + versionInformation; 423 | } 424 | } 425 | 426 | if (aumi.length() > SCHAR_MAX) { 427 | DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); 428 | } 429 | return aumi; 430 | } 431 | 432 | const std::wstring& WinToast::strerror(WinToastError error) { 433 | static const std::unordered_map Labels = { 434 | {WinToastError::NoError, L"No error. The process was executed correctly"}, 435 | {WinToastError::NotInitialized, L"The library has not been initialized"}, 436 | {WinToastError::SystemNotSupported, L"The OS does not support WinToast"}, 437 | {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"}, 438 | {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"}, 439 | {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"}, 440 | {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, 441 | {WinToastError::UnknownError, L"Unknown error"} 442 | }; 443 | 444 | const auto iter = Labels.find(error); 445 | assert(iter != Labels.end()); 446 | return iter->second; 447 | } 448 | 449 | enum WinToast::ShortcutResult WinToast::createShortcut() { 450 | if (_aumi.empty() || _appName.empty()) { 451 | DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); 452 | return SHORTCUT_MISSING_PARAMETERS; 453 | } 454 | 455 | if (!isCompatible()) { 456 | DEBUG_MSG(L"Your OS is not compatible with this library! =("); 457 | return SHORTCUT_INCOMPATIBLE_OS; 458 | } 459 | 460 | if (!_hasCoInitialized) { 461 | HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); 462 | if (initHr != RPC_E_CHANGED_MODE) { 463 | if (FAILED(initHr) && initHr != S_FALSE) { 464 | DEBUG_MSG(L"Error on COM library initialization!"); 465 | return SHORTCUT_COM_INIT_FAILURE; 466 | } 467 | else { 468 | _hasCoInitialized = true; 469 | } 470 | } 471 | } 472 | 473 | bool wasChanged; 474 | HRESULT hr = validateShellLinkHelper(wasChanged); 475 | if (SUCCEEDED(hr)) 476 | return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; 477 | 478 | hr = createShellLinkHelper(); 479 | return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; 480 | } 481 | 482 | bool WinToast::initialize(_Out_opt_ WinToastError* error) { 483 | _isInitialized = false; 484 | setError(error, WinToastError::NoError); 485 | 486 | if (!isCompatible()) { 487 | setError(error, WinToastError::SystemNotSupported); 488 | DEBUG_MSG(L"Error: system not supported."); 489 | return false; 490 | } 491 | 492 | 493 | if (_aumi.empty() || _appName.empty()) { 494 | setError(error, WinToastError::InvalidParameters); 495 | DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); 496 | return false; 497 | } 498 | 499 | if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) { 500 | if (createShortcut() < 0) { 501 | setError(error, WinToastError::ShellLinkNotCreated); 502 | DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); 503 | return false; 504 | } 505 | } 506 | 507 | if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { 508 | setError(error, WinToastError::InvalidAppUserModelID); 509 | DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); 510 | return false; 511 | } 512 | 513 | _isInitialized = true; 514 | return _isInitialized; 515 | } 516 | 517 | bool WinToast::isInitialized() const { 518 | return _isInitialized; 519 | } 520 | 521 | const std::wstring& WinToast::appName() const { 522 | return _appName; 523 | } 524 | 525 | const std::wstring& WinToast::appUserModelId() const { 526 | return _aumi; 527 | } 528 | 529 | 530 | HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { 531 | WCHAR path[MAX_PATH] = { L'\0' }; 532 | Util::defaultShellLinkPath(_appName, path); 533 | // Check if the file exist 534 | DWORD attr = GetFileAttributesW(path); 535 | if (attr >= 0xFFFFFFF) { 536 | DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); 537 | return E_FAIL; 538 | } 539 | 540 | // Let's load the file as shell link to validate. 541 | // - Create a shell link 542 | // - Create a persistant file 543 | // - Load the path as data for the persistant file 544 | // - Read the property AUMI and validate with the current 545 | // - Review if AUMI is equal. 546 | ComPtr shellLink; 547 | HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); 548 | if (SUCCEEDED(hr)) { 549 | ComPtr persistFile; 550 | hr = shellLink.As(&persistFile); 551 | if (SUCCEEDED(hr)) { 552 | hr = persistFile->Load(path, STGM_READWRITE); 553 | if (SUCCEEDED(hr)) { 554 | ComPtr propertyStore; 555 | hr = shellLink.As(&propertyStore); 556 | if (SUCCEEDED(hr)) { 557 | PROPVARIANT appIdPropVar; 558 | hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); 559 | if (SUCCEEDED(hr)) { 560 | WCHAR AUMI[MAX_PATH]; 561 | hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); 562 | wasChanged = false; 563 | if (FAILED(hr) || _aumi != AUMI) { 564 | if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { 565 | // AUMI Changed for the same app, let's update the current value! =) 566 | wasChanged = true; 567 | PropVariantClear(&appIdPropVar); 568 | hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); 569 | if (SUCCEEDED(hr)) { 570 | hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); 571 | if (SUCCEEDED(hr)) { 572 | hr = propertyStore->Commit(); 573 | if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { 574 | hr = persistFile->Save(path, TRUE); 575 | } 576 | } 577 | } 578 | } else { 579 | // Not allowed to touch the shortcut to fix the AUMI 580 | hr = E_FAIL; 581 | } 582 | } 583 | PropVariantClear(&appIdPropVar); 584 | } 585 | } 586 | } 587 | } 588 | } 589 | return hr; 590 | } 591 | 592 | 593 | 594 | HRESULT WinToast::createShellLinkHelper() { 595 | if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { 596 | return E_FAIL; 597 | } 598 | 599 | WCHAR exePath[MAX_PATH]{L'\0'}; 600 | WCHAR slPath[MAX_PATH]{L'\0'}; 601 | Util::defaultShellLinkPath(_appName, slPath); 602 | Util::defaultExecutablePath(exePath); 603 | ComPtr shellLink; 604 | HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); 605 | if (SUCCEEDED(hr)) { 606 | hr = shellLink->SetPath(exePath); 607 | if (SUCCEEDED(hr)) { 608 | hr = shellLink->SetArguments(L""); 609 | if (SUCCEEDED(hr)) { 610 | hr = shellLink->SetWorkingDirectory(exePath); 611 | if (SUCCEEDED(hr)) { 612 | ComPtr propertyStore; 613 | hr = shellLink.As(&propertyStore); 614 | if (SUCCEEDED(hr)) { 615 | PROPVARIANT appIdPropVar; 616 | hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); 617 | if (SUCCEEDED(hr)) { 618 | hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); 619 | if (SUCCEEDED(hr)) { 620 | hr = propertyStore->Commit(); 621 | if (SUCCEEDED(hr)) { 622 | ComPtr persistFile; 623 | hr = shellLink.As(&persistFile); 624 | if (SUCCEEDED(hr)) { 625 | hr = persistFile->Save(slPath, TRUE); 626 | } 627 | } 628 | } 629 | PropVariantClear(&appIdPropVar); 630 | } 631 | } 632 | } 633 | } 634 | } 635 | } 636 | return hr; 637 | } 638 | 639 | INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) { 640 | setError(error, WinToastError::NoError); 641 | INT64 id = -1; 642 | if (!isInitialized()) { 643 | setError(error, WinToastError::NotInitialized); 644 | DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); 645 | return id; 646 | } 647 | if (!handler) { 648 | setError(error, WinToastError::InvalidHandler); 649 | DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr."); 650 | return id; 651 | } 652 | 653 | ComPtr notificationManager; 654 | HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); 655 | if (SUCCEEDED(hr)) { 656 | ComPtr notifier; 657 | hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); 658 | if (SUCCEEDED(hr)) { 659 | ComPtr notificationFactory; 660 | hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); 661 | if (SUCCEEDED(hr)) { 662 | ComPtr xmlDocument; 663 | HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); 664 | if (SUCCEEDED(hr)) { 665 | for (UINT32 i = 0, fieldsCount = static_cast(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { 666 | hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); 667 | } 668 | 669 | // Modern feature are supported Windows > Windows 10 670 | if (SUCCEEDED(hr) && isSupportingModernFeatures()) { 671 | 672 | // Note that we do this *after* using toast.textFieldsCount() to 673 | // iterate/fill the template's text fields, since we're adding yet another text field. 674 | if (SUCCEEDED(hr) 675 | && !toast.attributionText().empty()) { 676 | hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); 677 | } 678 | 679 | std::array buf; 680 | for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) { 681 | _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i); 682 | hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data()); 683 | } 684 | 685 | if (SUCCEEDED(hr)) { 686 | hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) 687 | ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); 688 | } 689 | 690 | if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { 691 | hr = addDurationHelper(xmlDocument.Get(), 692 | (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); 693 | } 694 | 695 | if (SUCCEEDED(hr)) { 696 | hr = addScenarioHelper(xmlDocument.Get(), toast.scenario()); 697 | } 698 | 699 | } else { 700 | DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); 701 | } 702 | 703 | if (SUCCEEDED(hr)) { 704 | hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr; 705 | if (SUCCEEDED(hr)) { 706 | ComPtr notification; 707 | hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); 708 | if (SUCCEEDED(hr)) { 709 | INT64 expiration = 0, relativeExpiration = toast.expiration(); 710 | if (relativeExpiration > 0) { 711 | InternalDateTime expirationDateTime(relativeExpiration); 712 | expiration = expirationDateTime; 713 | hr = notification->put_ExpirationTime(&expirationDateTime); 714 | } 715 | 716 | if (SUCCEEDED(hr)) { 717 | hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); 718 | if (FAILED(hr)) { 719 | setError(error, WinToastError::InvalidHandler); 720 | } 721 | } 722 | 723 | if (SUCCEEDED(hr)) { 724 | GUID guid; 725 | hr = CoCreateGuid(&guid); 726 | if (SUCCEEDED(hr)) { 727 | id = guid.Data1; 728 | _buffer[id] = notification; 729 | DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); 730 | hr = notifier->Show(notification.Get()); 731 | if (FAILED(hr)) { 732 | setError(error, WinToastError::NotDisplayed); 733 | } 734 | } 735 | } 736 | } 737 | } 738 | } 739 | } 740 | } 741 | } 742 | } 743 | return FAILED(hr) ? -1 : id; 744 | } 745 | 746 | ComPtr WinToast::notifier(_In_ bool* succeded) const { 747 | ComPtr notificationManager; 748 | ComPtr notifier; 749 | HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); 750 | if (SUCCEEDED(hr)) { 751 | hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); 752 | } 753 | *succeded = SUCCEEDED(hr); 754 | return notifier; 755 | } 756 | 757 | bool WinToast::hideToast(_In_ INT64 id) { 758 | if (!isInitialized()) { 759 | DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); 760 | return false; 761 | } 762 | 763 | if (_buffer.find(id) != _buffer.end()) { 764 | auto succeded = false; 765 | auto notify = notifier(&succeded); 766 | if (succeded) { 767 | auto result = notify->Hide(_buffer[id].Get()); 768 | _buffer.erase(id); 769 | return SUCCEEDED(result); 770 | } 771 | } 772 | return false; 773 | } 774 | 775 | void WinToast::clear() { 776 | auto succeded = false; 777 | auto notify = notifier(&succeded); 778 | if (succeded) { 779 | auto end = _buffer.end(); 780 | for (auto it = _buffer.begin(); it != end; ++it) { 781 | notify->Hide(it->second.Get()); 782 | } 783 | _buffer.clear(); 784 | } 785 | } 786 | 787 | // 788 | // Available as of Windows 10 Anniversary Update 789 | // Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts 790 | // 791 | // NOTE: This will add a new text field, so be aware when iterating over 792 | // the toast's text fields or getting a count of them. 793 | // 794 | HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) { 795 | Util::createElement(xml, L"binding", L"text", { L"placement" }); 796 | ComPtr nodeList; 797 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); 798 | if (SUCCEEDED(hr)) { 799 | UINT32 nodeListLength; 800 | hr = nodeList->get_Length(&nodeListLength); 801 | if (SUCCEEDED(hr)) { 802 | for (UINT32 i = 0; i < nodeListLength; i++) { 803 | ComPtr textNode; 804 | hr = nodeList->Item(i, &textNode); 805 | if (SUCCEEDED(hr)) { 806 | ComPtr attributes; 807 | hr = textNode->get_Attributes(&attributes); 808 | if (SUCCEEDED(hr)) { 809 | ComPtr editedNode; 810 | if (SUCCEEDED(hr)) { 811 | hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); 812 | if (FAILED(hr) || !editedNode) { 813 | continue; 814 | } 815 | hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); 816 | if (SUCCEEDED(hr)) { 817 | return setTextFieldHelper(xml, text, i); 818 | } 819 | } 820 | } 821 | } 822 | } 823 | } 824 | } 825 | return hr; 826 | } 827 | 828 | HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) { 829 | ComPtr nodeList; 830 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); 831 | if (SUCCEEDED(hr)) { 832 | UINT32 length; 833 | hr = nodeList->get_Length(&length); 834 | if (SUCCEEDED(hr)) { 835 | ComPtr toastNode; 836 | hr = nodeList->Item(0, &toastNode); 837 | if (SUCCEEDED(hr)) { 838 | ComPtr toastElement; 839 | hr = toastNode.As(&toastElement); 840 | if (SUCCEEDED(hr)) { 841 | hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), 842 | WinToastStringWrapper(duration).Get()); 843 | } 844 | } 845 | } 846 | } 847 | return hr; 848 | } 849 | 850 | HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) { 851 | ComPtr nodeList; 852 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); 853 | if (SUCCEEDED(hr)) { 854 | UINT32 length; 855 | hr = nodeList->get_Length(&length); 856 | if (SUCCEEDED(hr)) { 857 | ComPtr toastNode; 858 | hr = nodeList->Item(0, &toastNode); 859 | if (SUCCEEDED(hr)) { 860 | ComPtr toastElement; 861 | hr = toastNode.As(&toastElement); 862 | if (SUCCEEDED(hr)) { 863 | hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), 864 | WinToastStringWrapper(scenario).Get()); 865 | } 866 | } 867 | } 868 | } 869 | return hr; 870 | } 871 | 872 | HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) { 873 | ComPtr nodeList; 874 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); 875 | if (SUCCEEDED(hr)) { 876 | ComPtr node; 877 | hr = nodeList->Item(pos, &node); 878 | if (SUCCEEDED(hr)) { 879 | hr = Util::setNodeStringValue(text, node.Get(), xml); 880 | } 881 | } 882 | return hr; 883 | } 884 | 885 | 886 | HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) { 887 | assert(path.size() < MAX_PATH); 888 | 889 | wchar_t imagePath[MAX_PATH] = L"file:///"; 890 | HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); 891 | if (SUCCEEDED(hr)) { 892 | ComPtr nodeList; 893 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); 894 | if (SUCCEEDED(hr)) { 895 | ComPtr node; 896 | hr = nodeList->Item(0, &node); 897 | if (SUCCEEDED(hr)) { 898 | ComPtr attributes; 899 | hr = node->get_Attributes(&attributes); 900 | if (SUCCEEDED(hr)) { 901 | ComPtr editedNode; 902 | hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); 903 | if (SUCCEEDED(hr)) { 904 | Util::setNodeStringValue(imagePath, editedNode.Get(), xml); 905 | } 906 | } 907 | } 908 | } 909 | } 910 | return hr; 911 | } 912 | 913 | HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) { 914 | std::vector attrs; 915 | if (!path.empty()) attrs.push_back(L"src"); 916 | if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop"); 917 | if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent"); 918 | Util::createElement(xml, L"toast", L"audio", attrs); 919 | 920 | ComPtr nodeList; 921 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); 922 | if (SUCCEEDED(hr)) { 923 | ComPtr node; 924 | hr = nodeList->Item(0, &node); 925 | if (SUCCEEDED(hr)) { 926 | ComPtr attributes; 927 | hr = node->get_Attributes(&attributes); 928 | if (SUCCEEDED(hr)) { 929 | ComPtr editedNode; 930 | if (!path.empty()) { 931 | if (SUCCEEDED(hr)) { 932 | hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); 933 | if (SUCCEEDED(hr)) { 934 | hr = Util::setNodeStringValue(path, editedNode.Get(), xml); 935 | } 936 | } 937 | } 938 | 939 | if (SUCCEEDED(hr)) { 940 | switch (option) { 941 | case WinToastTemplate::AudioOption::Loop: 942 | hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); 943 | if (SUCCEEDED(hr)) { 944 | hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); 945 | } 946 | break; 947 | case WinToastTemplate::AudioOption::Silent: 948 | hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); 949 | if (SUCCEEDED(hr)) { 950 | hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); 951 | } 952 | default: 953 | break; 954 | } 955 | } 956 | } 957 | } 958 | } 959 | return hr; 960 | } 961 | 962 | HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) { 963 | ComPtr nodeList; 964 | HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); 965 | if (SUCCEEDED(hr)) { 966 | UINT32 length; 967 | hr = nodeList->get_Length(&length); 968 | if (SUCCEEDED(hr)) { 969 | ComPtr actionsNode; 970 | if (length > 0) { 971 | hr = nodeList->Item(0, &actionsNode); 972 | } else { 973 | hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); 974 | if (SUCCEEDED(hr)) { 975 | hr = nodeList->get_Length(&length); 976 | if (SUCCEEDED(hr)) { 977 | ComPtr toastNode; 978 | hr = nodeList->Item(0, &toastNode); 979 | if (SUCCEEDED(hr)) { 980 | ComPtr toastElement; 981 | hr = toastNode.As(&toastElement); 982 | if (SUCCEEDED(hr)) 983 | hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); 984 | if (SUCCEEDED(hr)) 985 | hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); 986 | if (SUCCEEDED(hr)) { 987 | ComPtr actionsElement; 988 | hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); 989 | if (SUCCEEDED(hr)) { 990 | hr = actionsElement.As(&actionsNode); 991 | if (SUCCEEDED(hr)) { 992 | ComPtr appendedChild; 993 | hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); 994 | } 995 | } 996 | } 997 | } 998 | } 999 | } 1000 | } 1001 | if (SUCCEEDED(hr)) { 1002 | ComPtr actionElement; 1003 | hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); 1004 | if (SUCCEEDED(hr)) 1005 | hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); 1006 | if (SUCCEEDED(hr)) 1007 | hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); 1008 | if (SUCCEEDED(hr)) { 1009 | ComPtr actionNode; 1010 | hr = actionElement.As(&actionNode); 1011 | if (SUCCEEDED(hr)) { 1012 | ComPtr appendedChild; 1013 | hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); 1014 | } 1015 | } 1016 | } 1017 | } 1018 | } 1019 | return hr; 1020 | } 1021 | 1022 | void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value) { 1023 | if (error) { 1024 | *error = value; 1025 | } 1026 | } 1027 | 1028 | WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { 1029 | static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; 1030 | _textFields = std::vector(TextFieldsCount[type], L""); 1031 | } 1032 | 1033 | WinToastTemplate::~WinToastTemplate() { 1034 | _textFields.clear(); 1035 | } 1036 | 1037 | void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { 1038 | const auto position = static_cast(pos); 1039 | assert(position < _textFields.size()); 1040 | _textFields[position] = txt; 1041 | } 1042 | 1043 | void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { 1044 | _imagePath = imgPath; 1045 | } 1046 | 1047 | void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) { 1048 | _audioPath = audioPath; 1049 | } 1050 | 1051 | void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { 1052 | static const std::unordered_map Files = { 1053 | {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"}, 1054 | {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"}, 1055 | {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"}, 1056 | {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"}, 1057 | {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"}, 1058 | {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"}, 1059 | {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"}, 1060 | {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"}, 1061 | {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"}, 1062 | {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"}, 1063 | {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"}, 1064 | {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"}, 1065 | {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"}, 1066 | {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"}, 1067 | {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, 1068 | {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"}, 1069 | {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"}, 1070 | {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"}, 1071 | {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"}, 1072 | {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"}, 1073 | {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"}, 1074 | {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"}, 1075 | {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"}, 1076 | {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"}, 1077 | {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"}, 1078 | {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"}, 1079 | }; 1080 | const auto iter = Files.find(file); 1081 | assert(iter != Files.end()); 1082 | _audioPath = iter->second; 1083 | } 1084 | 1085 | void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { 1086 | _audioOption = audioOption; 1087 | } 1088 | 1089 | void WinToastTemplate::setFirstLine(_In_ const std::wstring &text) { 1090 | setTextField(text, WinToastTemplate::FirstLine); 1091 | } 1092 | 1093 | void WinToastTemplate::setSecondLine(_In_ const std::wstring &text) { 1094 | setTextField(text, WinToastTemplate::SecondLine); 1095 | } 1096 | 1097 | void WinToastTemplate::setThirdLine(_In_ const std::wstring &text) { 1098 | setTextField(text, WinToastTemplate::ThirdLine); 1099 | } 1100 | 1101 | void WinToastTemplate::setDuration(_In_ Duration duration) { 1102 | _duration = duration; 1103 | } 1104 | 1105 | void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { 1106 | _expiration = millisecondsFromNow; 1107 | } 1108 | 1109 | void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) { 1110 | switch (scenario) { 1111 | case Scenario::Default: _scenario = L"Default"; break; 1112 | case Scenario::Alarm: _scenario = L"Alarm"; break; 1113 | case Scenario::IncomingCall: _scenario = L"IncomingCall"; break; 1114 | case Scenario::Reminder: _scenario = L"Reminder"; break; 1115 | } 1116 | } 1117 | 1118 | void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) { 1119 | _attributionText = attributionText; 1120 | } 1121 | 1122 | void WinToastTemplate::addAction(_In_ const std::wstring & label) { 1123 | _actions.push_back(label); 1124 | } 1125 | 1126 | std::size_t WinToastTemplate::textFieldsCount() const { 1127 | return _textFields.size(); 1128 | } 1129 | 1130 | std::size_t WinToastTemplate::actionsCount() const { 1131 | return _actions.size(); 1132 | } 1133 | 1134 | bool WinToastTemplate::hasImage() const { 1135 | return _type < WinToastTemplateType::Text01; 1136 | } 1137 | 1138 | const std::vector& WinToastTemplate::textFields() const { 1139 | return _textFields; 1140 | } 1141 | 1142 | const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { 1143 | const auto position = static_cast(pos); 1144 | assert(position < _textFields.size()); 1145 | return _textFields[position]; 1146 | } 1147 | 1148 | const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const { 1149 | assert(position < _actions.size()); 1150 | return _actions[position]; 1151 | } 1152 | 1153 | const std::wstring& WinToastTemplate::imagePath() const { 1154 | return _imagePath; 1155 | } 1156 | 1157 | const std::wstring& WinToastTemplate::audioPath() const { 1158 | return _audioPath; 1159 | } 1160 | 1161 | const std::wstring& WinToastTemplate::attributionText() const { 1162 | return _attributionText; 1163 | } 1164 | 1165 | const std::wstring& WinToastLib::WinToastTemplate::scenario() const { 1166 | return _scenario; 1167 | } 1168 | 1169 | INT64 WinToastTemplate::expiration() const { 1170 | return _expiration; 1171 | } 1172 | 1173 | WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { 1174 | return _type; 1175 | } 1176 | 1177 | WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { 1178 | return _audioOption; 1179 | } 1180 | 1181 | WinToastTemplate::Duration WinToastTemplate::duration() const { 1182 | return _duration; 1183 | } 1184 | -------------------------------------------------------------------------------- /src/wintoastlib.h: -------------------------------------------------------------------------------- 1 | /* * Copyright (C) 2016-2019 Mohammed Boujemaoui 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | * this software and associated documentation files (the "Software"), to deal in 5 | * the Software without restriction, including without limitation the rights to 6 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | * the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in all 11 | * copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef WINTOASTLIB_H 22 | #define WINTOASTLIB_H 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | using namespace Microsoft::WRL; 42 | using namespace ABI::Windows::Data::Xml::Dom; 43 | using namespace ABI::Windows::Foundation; 44 | using namespace ABI::Windows::UI::Notifications; 45 | using namespace Windows::Foundation; 46 | 47 | 48 | namespace WinToastLib { 49 | 50 | class IWinToastHandler { 51 | public: 52 | enum WinToastDismissalReason { 53 | UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, 54 | ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, 55 | TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut 56 | }; 57 | virtual ~IWinToastHandler() = default; 58 | virtual void toastActivated() const = 0; 59 | virtual void toastActivated(int actionIndex) const = 0; 60 | virtual void toastDismissed(WinToastDismissalReason state) const = 0; 61 | virtual void toastFailed() const = 0; 62 | }; 63 | 64 | class WinToastTemplate { 65 | public: 66 | enum class Scenario { Default, Alarm, IncomingCall, Reminder }; 67 | enum Duration { System, Short, Long }; 68 | enum AudioOption { Default = 0, Silent, Loop }; 69 | enum TextField { FirstLine = 0, SecondLine, ThirdLine }; 70 | enum WinToastTemplateType { 71 | ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, 72 | ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, 73 | ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, 74 | ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, 75 | Text01 = ToastTemplateType::ToastTemplateType_ToastText01, 76 | Text02 = ToastTemplateType::ToastTemplateType_ToastText02, 77 | Text03 = ToastTemplateType::ToastTemplateType_ToastText03, 78 | Text04 = ToastTemplateType::ToastTemplateType_ToastText04, 79 | }; 80 | 81 | enum AudioSystemFile { 82 | DefaultSound, 83 | IM, 84 | Mail, 85 | Reminder, 86 | SMS, 87 | Alarm, 88 | Alarm2, 89 | Alarm3, 90 | Alarm4, 91 | Alarm5, 92 | Alarm6, 93 | Alarm7, 94 | Alarm8, 95 | Alarm9, 96 | Alarm10, 97 | Call, 98 | Call1, 99 | Call2, 100 | Call3, 101 | Call4, 102 | Call5, 103 | Call6, 104 | Call7, 105 | Call8, 106 | Call9, 107 | Call10, 108 | }; 109 | 110 | 111 | WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); 112 | ~WinToastTemplate(); 113 | 114 | void setFirstLine(_In_ const std::wstring& text); 115 | void setSecondLine(_In_ const std::wstring& text); 116 | void setThirdLine(_In_ const std::wstring& text); 117 | void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); 118 | void setAttributionText(_In_ const std::wstring& attributionText); 119 | void setImagePath(_In_ const std::wstring& imgPath); 120 | void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); 121 | void setAudioPath(_In_ const std::wstring& audioPath); 122 | void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); 123 | void setDuration(_In_ Duration duration); 124 | void setExpiration(_In_ INT64 millisecondsFromNow); 125 | void setScenario(_In_ Scenario scenario); 126 | void addAction(_In_ const std::wstring& label); 127 | 128 | std::size_t textFieldsCount() const; 129 | std::size_t actionsCount() const; 130 | bool hasImage() const; 131 | const std::vector& textFields() const; 132 | const std::wstring& textField(_In_ TextField pos) const; 133 | const std::wstring& actionLabel(_In_ std::size_t pos) const; 134 | const std::wstring& imagePath() const; 135 | const std::wstring& audioPath() const; 136 | const std::wstring& attributionText() const; 137 | const std::wstring& scenario() const; 138 | INT64 expiration() const; 139 | WinToastTemplateType type() const; 140 | WinToastTemplate::AudioOption audioOption() const; 141 | Duration duration() const; 142 | private: 143 | std::vector _textFields{}; 144 | std::vector _actions{}; 145 | std::wstring _imagePath{}; 146 | std::wstring _audioPath{}; 147 | std::wstring _attributionText{}; 148 | std::wstring _scenario{L"Default"}; 149 | INT64 _expiration{0}; 150 | AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; 151 | WinToastTemplateType _type{WinToastTemplateType::Text01}; 152 | Duration _duration{Duration::System}; 153 | }; 154 | 155 | class WinToast { 156 | public: 157 | enum WinToastError { 158 | NoError = 0, 159 | NotInitialized, 160 | SystemNotSupported, 161 | ShellLinkNotCreated, 162 | InvalidAppUserModelID, 163 | InvalidParameters, 164 | InvalidHandler, 165 | NotDisplayed, 166 | UnknownError 167 | }; 168 | 169 | enum ShortcutResult { 170 | SHORTCUT_UNCHANGED = 0, 171 | SHORTCUT_WAS_CHANGED = 1, 172 | SHORTCUT_WAS_CREATED = 2, 173 | 174 | SHORTCUT_MISSING_PARAMETERS = -1, 175 | SHORTCUT_INCOMPATIBLE_OS = -2, 176 | SHORTCUT_COM_INIT_FAILURE = -3, 177 | SHORTCUT_CREATE_FAILED = -4 178 | }; 179 | 180 | enum ShortcutPolicy { 181 | /* Don't check, create, or modify a shortcut. */ 182 | SHORTCUT_POLICY_IGNORE = 0, 183 | /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ 184 | SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, 185 | /* Require a shortcut with matching AUMI, create if missing, modify if not matching. 186 | * This is the default. */ 187 | SHORTCUT_POLICY_REQUIRE_CREATE = 2, 188 | }; 189 | 190 | WinToast(void); 191 | virtual ~WinToast(); 192 | static WinToast* instance(); 193 | static bool isCompatible(); 194 | static bool isSupportingModernFeatures(); 195 | static std::wstring configureAUMI(_In_ const std::wstring& companyName, 196 | _In_ const std::wstring& productName, 197 | _In_ const std::wstring& subProduct = std::wstring(), 198 | _In_ const std::wstring& versionInformation = std::wstring()); 199 | static const std::wstring& strerror(_In_ WinToastError error); 200 | virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); 201 | virtual bool isInitialized() const; 202 | virtual bool hideToast(_In_ INT64 id); 203 | virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_opt_ WinToastError* error = nullptr); 204 | virtual void clear(); 205 | virtual enum ShortcutResult createShortcut(); 206 | 207 | const std::wstring& appName() const; 208 | const std::wstring& appUserModelId() const; 209 | void setAppUserModelId(_In_ const std::wstring& aumi); 210 | void setAppName(_In_ const std::wstring& appName); 211 | void setShortcutPolicy(_In_ ShortcutPolicy policy); 212 | 213 | protected: 214 | bool _isInitialized{false}; 215 | bool _hasCoInitialized{false}; 216 | ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; 217 | std::wstring _appName{}; 218 | std::wstring _aumi{}; 219 | std::map> _buffer{}; 220 | 221 | HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); 222 | HRESULT createShellLinkHelper(); 223 | HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path); 224 | HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); 225 | HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos); 226 | HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); 227 | HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); 228 | HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); 229 | HRESULT addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario); 230 | ComPtr notifier(_In_ bool* succeded) const; 231 | void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); 232 | }; 233 | } 234 | #endif // WINTOASTLIB_H 235 | --------------------------------------------------------------------------------