Click here to Skip to main content
15,306,676 members
Articles / Programming Languages / C++
Article
Posted 16 Nov 2020

Tagged as

Stats

29.1K views
1.5K downloads
47 bookmarked

C++: Windows Toast Notification

Rate me:
Please Sign up or sign in to vote.
4.99/5 (26 votes)
14 Feb 2022CPOL5 min read
Windows Toast Notification in C++
In this article, you will learn the steps to use Windows Toast Notification library in C++.

Introduction

Windows Toast is a small window appearing bottom-right of the screen, is a common method whereby an application notifies its user an event of interest has occurred, for instance, an video encoding session has completed. Compared to MesssageBox() which shows a child modal window of your application is like a shove in your user's face especially when your application is currently not in the foreground. In this regard, Windows Toast is less intrusive. But to understand and use its complex API correctly is not an easy task.

WinToast by Mohammed Boujemaoui is an excellent Windows Toast Notification library that does a good job of hiding the complexity of showing a toast on Windows and most importantly, ease of use.

To get started, copy the wintoastlib.h and wintoastlib.cpp from the WinToast repo to your project and include its header and use the namespace, WinToastLib.

C++
#include "wintoastlib.h"

using namespace WinToastLib;

The second step is to implement the IWinToastHandler interface's toastActivated, toastDismissed and toastFailed virtual methods. The actionIndex parameter of toastActivated method returns the zero-based index of the button clicked.

C++
class WinToastHandler : public WinToastLib::IWinToastHandler
{
public:
    WinToastHandler(CDialogEx* pDialog) : m_pDialog(pDialog) {}
    // Public interfaces
    void toastActivated() const override {}
    void toastActivated(int actionIndex) const override {
        wchar_t buf[250];
        swprintf_s(buf, L"Button clicked: %d", actionIndex);
        m_pDialog->MessageBox(buf, L"info", MB_OK);
    }
    void toastDismissed(WinToastDismissalReason state) const override {}
    void toastFailed() const override {}
private:
    CDialogEx* m_pDialog;
};

Then add the concrete WinToastHandler as a member of your class.

C++
WinToastHandler m_WinToastHandler;

The third step is to configure AppUserModelId (AUMI) which consists of CompanyName, ProductName, SubProductName and VersionInformation. For UWP application, this step can be skipped as the UWP will fill up the information provided by your MSIX installer. For desktop applications like pure Win32 or MFC, this step is essential else the toast notification will fail to work.

C++
WinToast::WinToastError error;
WinToast::instance()->setAppName(L"TestToastExample");
const auto aumi = WinToast::configureAUMI
                  (L"company", L"wintoast", L"wintoastexample", L"20201012");
WinToast::instance()->setAppUserModelId(aumi);

if (!WinToast::instance()->initialize(&error)) {
    wchar_t buf[250];
    swprintf_s(buf, L"Failed to initialize WinToast :%d", error);
    MessageBox(buf, L"Error");
}

Your toast can consist of an image or a number of text lines based on the WinToastTemplateType enum passed to WinToastTemplate constructor. In case you are wondering what hero image is, hero image is the big image that appears at the top of the toast.

C++
enum WinToastTemplateType 
{
    ImageAndText01,
    ImageAndText02,
    ImageAndText03,
    ImageAndText04,
    Text01,
    Text02,
    Text03,
    Text04,
    HeroImageAndImageAndText01,
    HeroImageAndImageAndText02,
    HeroImageAndImageAndText03,
    HeroImageAndImageAndText04,
    HeroImageAndText01,
    HeroImageAndText02,
    HeroImageAndText03,
    HeroImageAndText04,
};

The last step is to show the toast to your user. We chose to show 1 image and 2 lines of text with ImageAndText02. With Windows 10 Anniversary Update, we have the option of adding a hero image that appears on the top or inlined by calling the constructor with HeroImageAndImageAndText02 and setHeroImagePath() with inlineImage set to true or false. setImagePath()'s second parameter can be set to CropHint::Circle to make the picture cropped in circle. This is only available with Windows 10 Anniversary Update. On older unsupported Windows 7/8/10, the picture is still displayed in a square. Two buttons, Yes and No are added through addAction().

C++
WinToastTemplate templ;
if (WinToast::isWin10AnniversaryOrHigher())
{
    templ = WinToastTemplate(WinToastTemplate::HeroImageAndImageAndText02);
    bool inlineImage = false;
    templ.setHeroImagePath(L"C:\\Users\\u\\Pictures\\hero.jpg", 
        inlineImage);
}
else
{
    templ = WinToastTemplate(WinToastTemplate::ImageAndText02);
}
templ.setImagePath(
    L"C:\\Users\\u\\Pictures\\pretty_gal.jpg", 
    WinToastTemplate::CropHint::Circle);

templ.setTextField(L"My First Toast", WinToastTemplate::FirstLine);
templ.setTextField(L"Say Hello?", WinToastTemplate::SecondLine);

templ.addAction(L"Yes");
templ.addAction(L"No");

// Read the additional options section in the article
templ.setDuration(WinToastTemplate::Duration::Short);
templ.setAudioOption(WinToastTemplate::AudioOption::Default);
templ.setAudioPath(WinToastTemplate::AudioSystemFile::Call1);

if (WinToast::instance()->showToast(templ, &m_WinToastHandler) == -1L)
{
    MessageBox(L"Could not launch your toast notification!", L"Error");
}

This is the screenshot of the toast notification. Be sure to change the image path and hero image path in the sample code to valid images on your system, else the toast will display a generic icon.

toast screenshot

This is the toast notification with a circle cropped image.

toast screenshot

This is the toast notification with a top hero image. Why my hero image is not a picture of a superhero but scenery?! Actually, I am not aware of the reason Microsoft named the top image, "hero", just like why the toast notification is called "toast". The latter could be due to the similarity to the toast popping up in the toaster when toasted.

toast screenshot with top hero image

This is the toast notification with a big inlined image in the middle.

toast screenshot with a big inlined image in the middle

Additional Options

  • Attribution text: you can add/remove the attribution text, by default is empty. Use WinToastTemplate::setAttributionText to modify it.
  • Duration: The amount of time the toast should display. This attribute can have one of the following values:
    • System: infinite display time until dismissed.
    • Short: default system short time configuration.
    • Long: default system long time configuration.
  • Audio Properties: you can modify the different behaviors of the sound:
    • Default: plays the audio file just one time.
    • Silent: turn off the sound.
    • Loop: plays the given sound in a loop during the toast existence.

WinToast allows the modification of the default audio file. Add the given file in to your projects resources (must be ms-appx:// or ms-appdata:// path) and define it by calling: WinToastTemplate::setAudioPath.

By default, WinToast checks if your systems support the features, ignoring the not supported ones.

Memory Leak Fix

When the article was first released in November 2020, its readers discovered memory leaks on Visual Studio. Using Deleaker, I found a HSTRING leak and callback function token leaks. I fixed the former and for the latter, I keep adding the tokens in _buffer map member because there is no good time to release those tokens. It silences the Visual Studio memory leak detection but the ongoing token accumulation is detrimental to a long running application. The token is used to remove the callback as a subscriber of change notifications when notifications are no longer needed. The crux of the problem is I cannot release the token while on callback due to its memory/resource still being in use. I contemplated using a worker thread to monitor the _buffer but it brings other synchronization and object lifetime problems. Windows Toast Notification is based on COM which has its own threading apartment detail to take care of. The worker thread approach is complex to implement and error-prone. In the callback, it is safe to delete the previous object (that holds the tokens) and marks the current object for next deletion. So I chose this simple approach over the worker thread.

After the fix, the task manager showed the memory of private working set and commit size increased for the first few toast notifications and stabilized afterwards. I have merged my fix with the upstream to do a pull request. Now waiting for original developer, Mohammed Boujemaoui to merge my PR. There is a breaking change in showToast(): handler type is changed from IWinToastHandler raw pointer to IWinToastHandler smart pointer, so update your code accordingly from

C++
if (WinToast::instance()->showToast(templ, &m_WinToastHandler) == -1L)
{...}

to

C++
std::shared_ptr<IWinToastHandler> handler(new WinToastHandler(this));
if (WinToast::instance()->showToast(templ, handler) == -1L)
{...}

History

  • 15th February, 2022: Fixed the memory leaks permanently. Read this section for information.
  • 4th December, 2021: Uploaded new demo which copies the images to the output directory during post-build event.
  • 28th January, 2021: Added image.zip for those who want to recreate the toast presented in this article. Remember to update the image path in the example code correctly.
  • 8th December, 2020: Added Additional Options section
  • 22th November, 2020: Fixed memory leaks and added hero image and circle cropped image features
  • 16th November, 2020: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shao Voon Wong
Software Developer (Senior)
Singapore Singapore
Shao Voon is from Singapore. CodeProject awarded him an MVP in recognition of his article contributions in 2019. He prefers to write applications based on 3rd party libraries than rolling out his own library. His interest lies primarily in computer graphics, software optimization, concurrency, security, and Agile methodologies.

Comments and Discussions

 
QuestionWindows Toast Notification Listener Pin
Kelissa Van Rustigger15-Mar-22 4:07
MemberKelissa Van Rustigger15-Mar-22 4:07 
AnswerRe: Windows Toast Notification Listener Pin
Shao Voon Wong15-Mar-22 16:01
mvaShao Voon Wong15-Mar-22 16:01 
QuestionSomething is going wrong here Pin
merano994-Dec-21 6:25
mvemerano994-Dec-21 6:25 
AnswerRe: Something is going wrong here Pin
Shao Voon Wong5-Dec-21 13:31
mvaShao Voon Wong5-Dec-21 13:31 
GeneralNo MessageBox and no Toast not solved Pin
merano996-Dec-21 11:19
mvemerano996-Dec-21 11:19 
GeneralRe: No MessageBox and no Toast not solved Pin
Shao Voon Wong6-Dec-21 14:38
mvaShao Voon Wong6-Dec-21 14:38 
QuestionHow to minimize this application (or Test Toast form) to System Tray? Pin
Nguyễn Văn Lượng10-Sep-21 7:39
MemberNguyễn Văn Lượng10-Sep-21 7:39 
QuestionHow do we read all unread toast notification currently have? Pin
sukeshchand5-May-21 20:43
professionalsukeshchand5-May-21 20:43 
AnswerRe: How do we read all unread toast notification currently have? Pin
Shao Voon Wong6-May-21 1:12
mvaShao Voon Wong6-May-21 1:12 
GeneralArticle formatting is wrong Pin
Shao Voon Wong29-Mar-21 19:19
mvaShao Voon Wong29-Mar-21 19:19 
GeneralMy vote of 5 Pin
Simon G4ELI25-Jan-21 21:03
MemberSimon G4ELI25-Jan-21 21:03 
GeneralMy vote of 4 Pin
Blake Miller18-Dec-20 12:46
MemberBlake Miller18-Dec-20 12:46 
NewsThe memory leaks are fixed. Pin
Shao Voon Wong21-Nov-20 22:53
mvaShao Voon Wong21-Nov-20 22:53 
QuestionProblem Pin
Michael Haephrati18-Nov-20 12:11
mvaMichael Haephrati18-Nov-20 12:11 
AnswerRe: Problem Pin
Shao Voon Wong18-Nov-20 18:32
mvaShao Voon Wong18-Nov-20 18:32 
GeneralRe: Problem Pin
Michael Haephrati18-Nov-20 22:58
mvaMichael Haephrati18-Nov-20 22:58 
GeneralRe: Problem Pin
Shao Voon Wong19-Nov-20 0:29
mvaShao Voon Wong19-Nov-20 0:29 
GeneralRe: Problem Pin
Michael Haephrati19-Nov-20 2:12
mvaMichael Haephrati19-Nov-20 2:12 
GeneralRe: Problem Pin
Shao Voon Wong19-Nov-20 2:45
mvaShao Voon Wong19-Nov-20 2:45 
GeneralRe: Problem Pin
Michael Haephrati19-Nov-20 3:26
mvaMichael Haephrati19-Nov-20 3:26 
GeneralRe: Problem Pin
Shao Voon Wong19-Nov-20 3:27
mvaShao Voon Wong19-Nov-20 3:27 
GeneralRe: Problem Pin
Michael Haephrati19-Nov-20 6:37
mvaMichael Haephrati19-Nov-20 6:37 
GeneralRe: Problem Pin
Shao Voon Wong19-Nov-20 17:59
mvaShao Voon Wong19-Nov-20 17:59 
GeneralRe: Problem Pin
Michael Haephrati20-Nov-20 0:22
mvaMichael Haephrati20-Nov-20 0:22 
GeneralRe: Problem Pin
Shao Voon Wong20-Nov-20 1:45
mvaShao Voon Wong20-Nov-20 1:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.