├── .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 | 
2 | 
3 | 
4 | 
5 | 
6 | 
7 |
8 | []()
9 | []()
10 | []()
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. |  |
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. |  |
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. |  |
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. |  |
45 | | `Text01` | Single string wrapped across three lines of text. | |
46 | | `Text02` | One string of bold text on the first line, one string of regular text wrapped across the second and third lines. |  |
47 | | `Text03` | One string of bold text wrapped across the first two lines, one string of regular text on the third line. | |
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. |  |
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 | 
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 | 
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. |  |
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. |  |
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. |  |
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. |  |
71 | | `Text01` | Single string wrapped across three lines of text. |  |
72 | | `Text02` | One string of bold text on the first line, one string of regular text wrapped across the second and third lines. |  |
73 | | `Text03` | One string of bold text wrapped across the first two lines, one string of regular text on the third line. |  |
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. |  |
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 | 
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 | 
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