From 9b5eed6b9106c26532772418fa650bd80d538850 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Wed, 14 Mar 2018 10:22:51 +0100 Subject: [PATCH 01/11] Update README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index c63df27..33e5927 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,19 @@ if (!WinToast::instance()->showToast(templ, handler)) { std::wcout << L"Error: Could not launch your toast notification!" << std::endl; } ``` + +## Expiration Time +Set the time after which a toast notification is no longer considered current or valid and should not be displayed. + +#### Remarks + + Windows attemps to raise toast notifications immediately after you + call Show, so this property is rarely used. + +> For Windows 8.x app, this property also causes the toast notification to be removed from the +> Action Center once the specified data and time is reached. + + ## Modern features - Windows 10 If your system support the new modern features (Version > Windows 8.1) available in Windows 10, you can add some interesting fields as: From 1017fcfbdcb45e6481776596af64f6dd75240f5f Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Thu, 22 Mar 2018 10:50:29 +0100 Subject: [PATCH 02/11] Adding the option to modify the duration of the toast. --- src/wintoastlib.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wintoastlib.h b/src/wintoastlib.h index ba1f832..61aa909 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -43,6 +43,7 @@ namespace WinToastLib { class WinToastTemplate { public: + enum Duration { Default, Short, Long }; enum AudioOption { Default = 0, Silent = 1, Loop = 2 }; enum TextField { FirstLine = 0, SecondLine, ThirdLine }; enum WinToastTemplateType { @@ -66,7 +67,8 @@ namespace WinToastLib { void setAudioOption(_In_ const WinToastTemplate::AudioOption& audioOption); void setAttributionText(_In_ const std::wstring & attributionText); void addAction(_In_ const std::wstring& label); - inline void setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } + void setDuration(_In_ const Duration Duration) { _duration = duration; } + void setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } inline int textFieldsCount() const { return static_cast(_textFields.size()); } inline int actionsCount() const { return static_cast(_actions.size()); } inline bool hasImage() const { return _type < Text01; } @@ -79,16 +81,17 @@ namespace WinToastLib { inline INT64 expiration() const { return _expiration; } inline WinToastTemplateType type() const { return _type; } inline WinToastTemplate::AudioOption audioOption() const { return _audioOption; } - + inline Duration duration() const { return _duration; } private: std::vector _textFields; std::wstring _imagePath; std::wstring _audioPath; std::vector _actions; INT64 _expiration; - WinToastTemplateType _type; + WinToastTemplateType _type = WinToastTemplateType::Text01; WinToastTemplate::AudioOption _audioOption = WinToastTemplate::AudioOption::Default; std::wstring _attributionText; + Duration _duration = Duration::Default; }; class WinToast { From 531794b6095a4cfec7cfe8af6852c79637795248 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Thu, 22 Mar 2018 11:11:16 +0100 Subject: [PATCH 03/11] Implementing duration support. --- src/wintoastlib.cpp | 28 ++++++++++++++++++++++++++++ src/wintoastlib.h | 3 ++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index 6099c2c..d1b1f97 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -614,9 +614,16 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan expiration = expirationDateTime; hr = notification->put_ExpirationTime(&expirationDateTime); } + + if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::Default) { + hr = addDurationHelper(xmlDocument.Get(), + (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); + } + if (SUCCEEDED(hr)) { hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); } + if (SUCCEEDED(hr)) { GUID guid; hr = CoCreateGuid(&guid); @@ -719,6 +726,27 @@ HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ con return hr; } +HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); + if (SUCCEEDED(hr)) { + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr toastElement; + hr = toastNode.As(&toastElement); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), + WinToastStringWrapper(labels[duration]).Get()); + } + } + } + } + return hr; +} + HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ int pos) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); diff --git a/src/wintoastlib.h b/src/wintoastlib.h index 61aa909..e6c39b5 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -141,7 +141,8 @@ namespace WinToastLib { HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ int pos); HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); - ComPtr notifier(_In_ bool* succeded) const; + HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); + ComPtr notifier(_In_ bool* succeded) const; }; } #endif // WINTOASTLIB_H From bd8b53e585ede4967e756ba83f7b81095941525f Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Date: Sat, 31 Mar 2018 12:07:01 +0200 Subject: [PATCH 04/11] Integration of the duration field. Cleaning the code. --- example/console-example/main.cpp | 6 +- src/wintoastlib.cpp | 74 ++++++++++++++++++++++-- src/wintoastlib.h | 97 ++++++++++++++++---------------- 3 files changed, 120 insertions(+), 57 deletions(-) diff --git a/example/console-example/main.cpp b/example/console-example/main.cpp index f2f394d..b4a1f46 100644 --- a/example/console-example/main.cpp +++ b/example/console-example/main.cpp @@ -108,7 +108,7 @@ int wmain(int argc, LPWSTR *argv) bool onlyCreateShortcut = false; - WinToastTemplate::AudioOption audioOption = WinToastTemplate::Default; + WinToastTemplate::AudioOption audioOption = WinToastTemplate::AudioOption::Default; int i; for (i = 1; i < argc; i++) @@ -159,8 +159,8 @@ int wmain(int argc, LPWSTR *argv) } bool withImage = (imagePath != NULL); - WinToastTemplate templ( withImage ? WinToastTemplate::ImageAndText02 : WinToastTemplate::Text02); - templ.setTextField(text, WinToastTemplate::FirstLine); + WinToastTemplate templ( withImage ? WinToastTemplate::WinToastTemplateType::ImageAndText02 : WinToastTemplate::WinToastTemplateType::Text02); + templ.setTextField(text, WinToastTemplate::TextField::FirstLine); templ.setAudioOption(audioOption); templ.setAttributionText(attribute); diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index d1b1f97..155ba0b 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -595,7 +595,7 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan } if (SUCCEEDED(hr)) { - hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::Default) + hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); } } else { @@ -730,6 +730,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { + UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr toastNode; @@ -739,7 +740,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), - WinToastStringWrapper(labels[duration]).Get()); + WinToastStringWrapper(duration).Get()); } } } @@ -897,7 +898,8 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { static const std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[_type], L""); + const std::size_t position = static_cast::type>(type); + _textFields = std::vector(TextFieldsCount[position], L""); } WinToastTemplate::~WinToastTemplate() { @@ -905,7 +907,8 @@ WinToastTemplate::~WinToastTemplate() { } void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { - _textFields[pos] = txt; + const std::size_t position = static_cast::type>(pos); + _textFields[position] = txt; } void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { @@ -916,10 +919,18 @@ void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) { _audioPath = audioPath; } -void WinToastTemplate::setAudioOption(_In_ const WinToastTemplate::AudioOption & audioOption) { +void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { _audioOption = audioOption; } +void WinToastTemplate::setDuration(_In_ Duration duration) { + _duration = duration; +} + +void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { + _expiration = millisecondsFromNow; +} + void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) { _attributionText = attributionText; } @@ -928,3 +939,56 @@ void WinToastTemplate::addAction(_In_ const std::wstring & label) { _actions.push_back(label); } + +std::size_t WinToastTemplate::textFieldsCount() const { + return _textFields.size(); +} + +std::size_t WinToastTemplate::actionsCount() const { + return _actions.size(); +} + +bool WinToastTemplate::hasImage() const { + return _type < WinToastTemplateType::Text01; +} + +const std::vector& WinToastTemplate::textFields() const { + return _textFields; +} + +const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { + const std::size_t position = static_cast::type>(pos); + return _textFields[position]; +} + +const std::wstring& WinToastTemplate::actionLabel(_In_ int pos) const { + return _actions[pos]; +} + +const std::wstring& WinToastTemplate::imagePath() const { + return _imagePath; +} + +const std::wstring& WinToastTemplate::audioPath() const { + return _audioPath; +} + +const std::wstring& WinToastTemplate::attributionText() const { + return _attributionText; +} + +INT64 WinToastTemplate::expiration() const { + return _expiration; +} + +WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { + return _type; +} + +WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { + return _audioOption; +} + +WinToastTemplate::Duration WinToastTemplate::duration() const { + return _duration; +} \ No newline at end of file diff --git a/src/wintoastlib.h b/src/wintoastlib.h index 88a4bd3..fc36ef3 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -43,10 +43,10 @@ namespace WinToastLib { class WinToastTemplate { public: - enum Duration { Default, Short, Long }; - enum AudioOption { Default = 0, Silent = 1, Loop = 2 }; - enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - enum WinToastTemplateType { + enum class Duration { Default, Short, Long }; + enum class AudioOption { Default = 0, Silent = 1, Loop = 2 }; + enum class TextField { FirstLine = 0, SecondLine, ThirdLine }; + enum class WinToastTemplateType { ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, @@ -58,40 +58,39 @@ namespace WinToastLib { WinToastTemplateTypeCount }; - WinToastTemplate(_In_ WinToastTemplateType type = ImageAndText02); + WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); ~WinToastTemplate(); - void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); - void setImagePath(_In_ const std::wstring& imgPath); - void setAudioPath(_In_ const std::wstring& audioPath); - void setAudioOption(_In_ const WinToastTemplate::AudioOption& audioOption); - void setAttributionText(_In_ const std::wstring & attributionText); - void addAction(_In_ const std::wstring& label); - void setDuration(_In_ const Duration Duration) { _duration = duration; } - void setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } - inline int textFieldsCount() const { return static_cast(_textFields.size()); } - inline int actionsCount() const { return static_cast(_actions.size()); } - inline bool hasImage() const { return _type < Text01; } - inline std::vector textFields() const { return _textFields; } - inline std::wstring textField(_In_ TextField pos) const { return _textFields[pos]; } - inline std::wstring actionLabel(_In_ int pos) const { return _actions[pos]; } - inline std::wstring imagePath() const { return _imagePath; } - inline std::wstring audioPath() const { return _audioPath; } - inline std::wstring attributionText() const { return _attributionText; } - inline INT64 expiration() const { return _expiration; } - inline WinToastTemplateType type() const { return _type; } - inline WinToastTemplate::AudioOption audioOption() const { return _audioOption; } - inline Duration duration() const { return _duration; } + void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); + void setImagePath(_In_ const std::wstring& imgPath); + void setAudioPath(_In_ const std::wstring& audioPath); + void setAttributionText(_In_ const std::wstring & attributionText); + void addAction(_In_ const std::wstring& label); + void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); + void setDuration(_In_ Duration duration); + void setExpiration(_In_ INT64 millisecondsFromNow); + std::size_t textFieldsCount() const; + std::size_t actionsCount() const; + bool hasImage() const; + const std::vector& textFields() const; + const std::wstring& textField(_In_ TextField pos) const; + const std::wstring& actionLabel(_In_ int pos) const; + const std::wstring& imagePath() const; + const std::wstring& audioPath() const; + const std::wstring& attributionText() const; + INT64 expiration() const; + WinToastTemplateType type() const; + WinToastTemplate::AudioOption audioOption() const; + Duration duration() const; private: std::vector _textFields; - std::wstring _imagePath; - std::wstring _audioPath; std::vector _actions; - INT64 _expiration; - WinToastTemplateType _type = WinToastTemplateType::Text01; + std::wstring _imagePath = L""; + std::wstring _audioPath = L""; + std::wstring _attributionText = L""; INT64 _expiration = 0; - WinToastTemplate::AudioOption _audioOption = WinToastTemplate::AudioOption::Default; - std::wstring _attributionText; + AudioOption _audioOption = WinToastTemplate::AudioOption::Default; + WinToastTemplateType _type = WinToastTemplateType::Text01; Duration _duration = Duration::Default; }; @@ -107,15 +106,15 @@ namespace WinToastLib { _In_ const std::wstring& subProduct = std::wstring(), _In_ const std::wstring& versionInformation = std::wstring() ); - virtual bool initialize(); - virtual bool isInitialized() const { return _isInitialized; } - virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler); - virtual bool hideToast(_In_ INT64 id); - virtual void clear(); - inline std::wstring appName() const { return _appName; } - inline std::wstring appUserModelId() const { return _aumi; } - void setAppUserModelId(_In_ const std::wstring& appName); - void setAppName(_In_ const std::wstring& appName); + virtual bool initialize(); + virtual bool isInitialized() const { return _isInitialized; } + virtual bool hideToast(_In_ INT64 id); + virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler); + virtual void clear(); + const std::wstring& appName() const { return _appName; } + const std::wstring& appUserModelId() const { return _aumi; } + void setAppUserModelId(_In_ const std::wstring& appName); + void setAppName(_In_ const std::wstring& appName); enum ShortcutResult { SHORTCUT_UNCHANGED = 0, @@ -135,14 +134,14 @@ namespace WinToastLib { std::wstring _aumi; std::map> _buffer; - HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path); - HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ int pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); - HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); - HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); + HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); + HRESULT createShellLinkHelper(); + HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path); + HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); + HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ int pos); + HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text); + HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); + HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); ComPtr notifier(_In_ bool* succeded) const; }; } From 017cf350c1350a3ff2afabe95bfdf89d9e73da05 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Date: Sat, 31 Mar 2018 12:07:31 +0200 Subject: [PATCH 05/11] Updating example project to use MVSC 2017 --- example/console-example/WinToast Console Example.sln | 9 ++++++--- example/console-example/WinToast Console Example.vcxproj | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/example/console-example/WinToast Console Example.sln b/example/console-example/WinToast Console Example.sln index d33b715..e09f9ce 100644 --- a/example/console-example/WinToast Console Example.sln +++ b/example/console-example/WinToast Console Example.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinToast Console Example", "WinToast Console Example.vcxproj", "{7C5AC60D-8668-47B6-9D05-FB363F854065}" EndProject @@ -13,8 +13,8 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.ActiveCfg = Debug|x64 - {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.Build.0 = Debug|x64 + {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.ActiveCfg = Debug|Win32 + {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x64.Build.0 = Debug|Win32 {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x86.ActiveCfg = Debug|Win32 {7C5AC60D-8668-47B6-9D05-FB363F854065}.Debug|x86.Build.0 = Debug|Win32 {7C5AC60D-8668-47B6-9D05-FB363F854065}.Release|x64.ActiveCfg = Release|x64 @@ -25,4 +25,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ABA98BF8-B665-455B-A51E-B36DD616139C} + EndGlobalSection EndGlobal diff --git a/example/console-example/WinToast Console Example.vcxproj b/example/console-example/WinToast Console Example.vcxproj index 055e197..a483531 100644 --- a/example/console-example/WinToast Console Example.vcxproj +++ b/example/console-example/WinToast Console Example.vcxproj @@ -22,13 +22,13 @@ 15.0 {7C5AC60D-8668-47B6-9D05-FB363F854065} Win32Proj - 8.1 + 10.0.16299.0 Application true - v140 + v141 From 3690f7001c386685466d97a33d382cfb867ea3f1 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Date: Sat, 31 Mar 2018 23:03:59 +0200 Subject: [PATCH 06/11] Restoring original behavior --- example/console-example/main.cpp | 4 ++-- src/wintoastlib.cpp | 15 ++++++--------- src/wintoastlib.h | 16 ++++++++-------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/example/console-example/main.cpp b/example/console-example/main.cpp index b4a1f46..e1f4801 100644 --- a/example/console-example/main.cpp +++ b/example/console-example/main.cpp @@ -159,8 +159,8 @@ int wmain(int argc, LPWSTR *argv) } bool withImage = (imagePath != NULL); - WinToastTemplate templ( withImage ? WinToastTemplate::WinToastTemplateType::ImageAndText02 : WinToastTemplate::WinToastTemplateType::Text02); - templ.setTextField(text, WinToastTemplate::TextField::FirstLine); + WinToastTemplate templ( withImage ? WinToastTemplate::ImageAndText02 : WinToastTemplate::Text02); + templ.setTextField(text, WinToastTemplate::FirstLine); templ.setAudioOption(audioOption); templ.setAttributionText(attribute); diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index 155ba0b..87a2e87 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -373,7 +373,7 @@ bool WinToast::isCompatible() { || (DllImporter::WindowsDeleteString == nullptr)); } -bool WinToastLib::WinToast::supportModernFeatures() { +bool WinToastLib::WinToast::isSupportingModernFeatures() { RTL_OSVERSIONINFOW tmp = GetRealOSVersion(); return tmp.dwMajorVersion > 6; @@ -578,7 +578,7 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan } // Modern feature are supported Windows > Windows 10 - if (SUCCEEDED(hr) && supportModernFeatures()) { + if (SUCCEEDED(hr) && isSupportingModernFeatures()) { // Note that we do this *after* using toast.textFieldsCount() to // iterate/fill the template's text fields, since we're adding yet another text field. @@ -615,7 +615,7 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan hr = notification->put_ExpirationTime(&expirationDateTime); } - if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::Default) { + if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { hr = addDurationHelper(xmlDocument.Get(), (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); } @@ -898,8 +898,7 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { static const std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; - const std::size_t position = static_cast::type>(type); - _textFields = std::vector(TextFieldsCount[position], L""); + _textFields = std::vector(TextFieldsCount[type], L""); } WinToastTemplate::~WinToastTemplate() { @@ -907,8 +906,7 @@ WinToastTemplate::~WinToastTemplate() { } void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { - const std::size_t position = static_cast::type>(pos); - _textFields[position] = txt; + _textFields[pos] = txt; } void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { @@ -957,8 +955,7 @@ const std::vector& WinToastTemplate::textFields() const { } const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { - const std::size_t position = static_cast::type>(pos); - return _textFields[position]; + return _textFields[pos]; } const std::wstring& WinToastTemplate::actionLabel(_In_ int pos) const { diff --git a/src/wintoastlib.h b/src/wintoastlib.h index fc36ef3..c12901b 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -43,10 +43,10 @@ namespace WinToastLib { class WinToastTemplate { public: - enum class Duration { Default, Short, Long }; - enum class AudioOption { Default = 0, Silent = 1, Loop = 2 }; - enum class TextField { FirstLine = 0, SecondLine, ThirdLine }; - enum class WinToastTemplateType { + enum Duration { System, Short, Long }; + enum AudioOption { Default = 0, Silent = 1, Loop = 2 }; + enum TextField { FirstLine = 0, SecondLine, ThirdLine }; + enum WinToastTemplateType { ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, @@ -91,7 +91,7 @@ namespace WinToastLib { INT64 _expiration = 0; AudioOption _audioOption = WinToastTemplate::AudioOption::Default; WinToastTemplateType _type = WinToastTemplateType::Text01; - Duration _duration = Duration::Default; + Duration _duration = Duration::System; }; class WinToast { @@ -99,9 +99,9 @@ namespace WinToastLib { WinToast(void); virtual ~WinToast(); static WinToast* instance(); - static bool isCompatible(); - static bool supportModernFeatures(); - static std::wstring configureAUMI(_In_ const std::wstring& companyName, + static bool isCompatible(); + static bool isSupportingModernFeatures(); + static std::wstring configureAUMI(_In_ const std::wstring& companyName, _In_ const std::wstring& productName, _In_ const std::wstring& subProduct = std::wstring(), _In_ const std::wstring& versionInformation = std::wstring() From 12494deff691d484e199dc0f7915eb8924a02fef Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Date: Sun, 1 Apr 2018 12:48:47 +0200 Subject: [PATCH 07/11] Implementing the minimum support to handle errors. --- src/wintoastlib.cpp | 105 +++++++++++++++++++++++++++++++------------- src/wintoastlib.h | 47 +++++++++++++------- 2 files changed, 105 insertions(+), 47 deletions(-) diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index 87a2e87..780733b 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -429,18 +429,33 @@ enum WinToast::ShortcutResult WinToast::createShortcut() { return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; hr = createShellLinkHelper(); - if (SUCCEEDED(hr)) - return SHORTCUT_WAS_CREATED; - return SHORTCUT_CREATE_FAILED; + return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; } -bool WinToast::initialize() { +bool WinToast::initialize(_Out_ WinToastError* error) { _isInitialized = false; - if (createShortcut() < 0) + if (!isCompatible()) { + setError(error, WinToastError::SystemNotSupported); + DEBUG_MSG(L"Error: system not supported."); + return false; + } + + + if (_aumi.empty() || _appName.empty()) { + setError(error, WinToastError::InvalidParameters); + DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); return false; + } + + if (createShortcut() < 0) { + setError(error, WinToastError::ShellLinkNotCreated); + DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); + return false; + } if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { + setError(error, WinToastError::InvalidAppUserModelID); DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); return false; } @@ -449,6 +464,19 @@ bool WinToast::initialize() { return _isInitialized; } +bool WinToast::isInitialized() const { + return _isInitialized; +} + +const std::wstring& WinToast::appName() const { + return _appName; +} + +const std::wstring& WinToast::appUserModelId() const { + return _aumi; +} + + HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { WCHAR path[MAX_PATH] = { L'\0' }; Util::defaultShellLinkPath(_appName, path); @@ -549,14 +577,17 @@ HRESULT WinToast::createShellLinkHelper() { return hr; } -INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler) { +INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) { + setError(error, WinToastError::NoError); INT64 id = -1; if (!isInitialized()) { - DEBUG_MSG("Error when launching the toast. WinToast is not initialized =("); + setError(error, WinToastError::NotInitialized); + DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); return id; } if (!handler) { - DEBUG_MSG("Error when launching the toast. handler cannot be null."); + setError(error, WinToastError::InvalidHandler); + DEBUG_MSG("Error when launching the toast. Handler cannot be null."); return id; } @@ -598,6 +629,12 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); } + + if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { + hr = addDurationHelper(xmlDocument.Get(), + (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); + } + } else { DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); } @@ -615,13 +652,11 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan hr = notification->put_ExpirationTime(&expirationDateTime); } - if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { - hr = addDurationHelper(xmlDocument.Get(), - (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); - } - if (SUCCEEDED(hr)) { hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); + if (FAILED(hr)) { + setError(error, WinToastError::InvalidHandler); + } } if (SUCCEEDED(hr)) { @@ -632,6 +667,9 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHan _buffer[id] = notification; DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); hr = notifier->Show(notification.Get()); + if (FAILED(hr)) { + setError(error, WinToastError::NotDisplayed); + } } } } @@ -655,7 +693,6 @@ ComPtr WinToast::notifier(_In_ bool* succeded) const { return notifier; } - bool WinToast::hideToast(_In_ INT64 id) { if (!isInitialized()) { DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); @@ -808,27 +845,27 @@ HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::ws if (SUCCEEDED(hr)) { hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); if (SUCCEEDED(hr)) { - Util::setNodeStringValue(path, editedNode.Get(), xml); + hr = Util::setNodeStringValue(path, editedNode.Get(), xml); } } } - // - // These options are mutually exclusive - // - switch (option) { - case WinToastTemplate::AudioOption::Loop: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - break; - case WinToastTemplate::AudioOption::Silent: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(L"true", editedNode.Get(), xml); + + if (SUCCEEDED(hr)) { + switch (option) { + case WinToastTemplate::AudioOption::Loop: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + break; + case WinToastTemplate::AudioOption::Silent: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + default: + break; } - default: - break; } } } @@ -896,6 +933,12 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin return hr; } +void WinToast::setError(_Out_ WinToastError* error, _In_ WinToastError value) { + if (error) { + *error = value; + } +} + WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { static const std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3}; _textFields = std::vector(TextFieldsCount[type], L""); diff --git a/src/wintoastlib.h b/src/wintoastlib.h index c12901b..6f077ae 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -96,6 +96,29 @@ namespace WinToastLib { class WinToast { public: + enum WinToastError { + NoError, + NotInitialized, + SystemNotSupported, + ShellLinkNotCreated, + InvalidAppUserModelID, + InvalidParameters, + InvalidHandler, + NotDisplayed, + UnknownError + }; + + enum ShortcutResult { + SHORTCUT_UNCHANGED = 0, + SHORTCUT_WAS_CHANGED = 1, + SHORTCUT_WAS_CREATED = 2, + + SHORTCUT_MISSING_PARAMETERS = -1, + SHORTCUT_INCOMPATIBLE_OS = -2, + SHORTCUT_COM_INIT_FAILURE = -3, + SHORTCUT_CREATE_FAILED = -4 + }; + WinToast(void); virtual ~WinToast(); static WinToast* instance(); @@ -106,27 +129,18 @@ namespace WinToastLib { _In_ const std::wstring& subProduct = std::wstring(), _In_ const std::wstring& versionInformation = std::wstring() ); - virtual bool initialize(); - virtual bool isInitialized() const { return _isInitialized; } + virtual bool initialize(_Out_ WinToastError* error = nullptr); + virtual bool isInitialized() const; virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler); + virtual INT64 showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error = nullptr); virtual void clear(); - const std::wstring& appName() const { return _appName; } - const std::wstring& appUserModelId() const { return _aumi; } + virtual enum ShortcutResult createShortcut(); + + const std::wstring& appName() const; + const std::wstring& appUserModelId() const; void setAppUserModelId(_In_ const std::wstring& appName); void setAppName(_In_ const std::wstring& appName); - enum ShortcutResult { - SHORTCUT_UNCHANGED = 0, - SHORTCUT_WAS_CHANGED = 1, - SHORTCUT_WAS_CREATED = 2, - - SHORTCUT_MISSING_PARAMETERS = -1, - SHORTCUT_INCOMPATIBLE_OS = -2, - SHORTCUT_COM_INIT_FAILURE = -3, - SHORTCUT_CREATE_FAILED = -4 - }; - virtual enum ShortcutResult createShortcut(); protected: bool _isInitialized; bool _hasCoInitialized; @@ -143,6 +157,7 @@ namespace WinToastLib { HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& action, _In_ const std::wstring& arguments); HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration); ComPtr notifier(_In_ bool* succeded) const; + void setError(_Out_ WinToastError* error, _In_ WinToastError value); }; } #endif // WINTOASTLIB_H From a6a86a34ba4942f94a6f98a157cf72f41882a24a Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Date: Sun, 1 Apr 2018 13:18:26 +0200 Subject: [PATCH 08/11] Initialize the error with a default value. --- src/wintoastlib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wintoastlib.h b/src/wintoastlib.h index 6f077ae..46eab51 100644 --- a/src/wintoastlib.h +++ b/src/wintoastlib.h @@ -97,7 +97,7 @@ namespace WinToastLib { class WinToast { public: enum WinToastError { - NoError, + NoError = 0, NotInitialized, SystemNotSupported, ShellLinkNotCreated, From c9d69172ec58894e5d6a10c9697c9ff009dbf8d4 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Sun, 1 Apr 2018 13:21:34 +0200 Subject: [PATCH 09/11] Update README.md --- README.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6955a96..24f30ad 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,11 @@ WinToast is a lightly library written in C++ which brings a complete integration 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. 1. [Toast Templates](#id1) -2. [Event Handler](#id2) -3. [Expiration Time](#id3) -4. [Modern Features](#id4) -5. [Example of usage](#id5) +2. [Error Handling](#id2) +3. [Event Handler](#id3) +4. [Expiration Time](#id4) +5. [Modern Features](#id5) +6. [Example of usage](#id6)
@@ -47,7 +48,16 @@ templ.setImagePath(L"C:/example.png"); templ.setTextField(L"title", WinToastTemplate::FirstLine); templ.setTextField(L"subtitle", WinToastTemplate::SecondLine); ``` -
+ +*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.* + +
+## Error Handling + + + + +
## Event Handler @@ -72,7 +82,7 @@ class WinToastHandlerExample : public IWinToastHandler { void toastFailed() const; }; ``` -
+
## Expiration Time @@ -81,7 +91,7 @@ Set the time after which a toast notification is no longer considered current or > For Windows 8.x app, this property also causes the toast notification to be removed from the > Action Center once the specified data and time is reached. -
+
## Modern features - Windows 10 @@ -104,6 +114,10 @@ WinToast::instance()->showToast(templ, handler) !["Toast with some actions"](https://lh3.googleusercontent.com/uJE_H0aBisOZ-9GynEWgA7Hha8tHEI-i0aHrFuOFDBsPSD-IJ-qEN0Y7XY4VI5hp_5MQ9xjWbFcm) - **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*: default system configuration. + - *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. @@ -115,7 +129,7 @@ WinToast::instance()->showToast(templ, handler) ***By default, WinToast checks if your systems support the features, ignoring the not supported ones.*** -
+
## Example of Usage From 482e0ac8d0d8e0ec2564f8294b979bf214a6806e Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Sun, 1 Apr 2018 13:39:27 +0200 Subject: [PATCH 10/11] Update README.md --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 24f30ad..fcc630a 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ WinToast is a lightly library written in C++ which brings a complete integration 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. 1. [Toast Templates](#id1) -2. [Error Handling](#id2) -3. [Event Handler](#id3) -4. [Expiration Time](#id4) -5. [Modern Features](#id5) +2. [Event Handler](#id3) +3. [Expiration Time](#id4) +4. [Modern Features](#id5) +5. [Error Handling](#id2) 6. [Example of usage](#id6)
@@ -51,12 +51,6 @@ templ.setTextField(L"subtitle", WinToastTemplate::SecondLine); *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.* -
-## Error Handling - - - -
## Event Handler @@ -129,6 +123,46 @@ WinToast::instance()->showToast(templ, handler) ***By default, WinToast checks if your systems support the features, ignoring the not supported ones.*** +
+ + ## Error Handling +There are several reasons WinToast can fail that's why the library notifies caller about fail reason. Those are the code for each failure: + +| WinToastError | Error Code | Error message | +|--|--|--| +| NoError | 0x00 | No error. The process was executed correctly | +| NotInitialized | 0x01 | The library has not been initialized | +| SystemNotSupported | 0x02 | The OS does not support WinToast | +| ShellLinkNotCreated | 0x03 | The library was not able to create a Shell Link for the app | +| InvalidAppUserModelID | 0x04 | The AUMI is not a valid one | +| InvalidParameters | 0x05 | The parameters used to configure the library are not valid normally because an invalid AUMI or App Name | +| NotDisplayed | 0x06 | The toast was created correctly but WinToast was not able to display the toast | +| UnknownError | 0x07 | Unknown error | + +A common example of usage is to check while initializing the library or showing a toast notification the possible failure code: + +```cpp +WinToast::WinToastError error; +const bool succedded = WinToast::instance()->initialize(&error); +if (!succedded) { + std::wcout << L"Error, could not initialize the lib. Error number: " + << error << std::endl; +} +... +// Configure the template +... +const bool launched = WinToast::instance()->showToast(templ, handler, &error); +if (!launched) { + std::wcout << L"Error: Could not launch your toast notification. Error: " + << error << std::endl; +} + ``` + + + + + +
## Example of Usage From f9a86f3e96538040c3801fec07d290879184ce89 Mon Sep 17 00:00:00 2001 From: Mohammed Boujemaoui Boulaghmoudi Date: Sun, 1 Apr 2018 13:56:20 +0200 Subject: [PATCH 11/11] Update README.md --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fcc630a..f533da7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ Toast notifications allows your app to inform the users about relevant informati 3. [Expiration Time](#id4) 4. [Modern Features](#id5) 5. [Error Handling](#id2) -6. [Example of usage](#id6) +6. [Windows 10 Configuration](#id7) +7. [Example of usage](#id6) +
@@ -158,11 +160,6 @@ if (!launched) { } ``` - - - - -
## Example of Usage @@ -214,8 +211,15 @@ if (!WinToast::instance()->showToast(templ, handler)) { std::wcout << L"Error: Could not launch your toast notification!" << std::endl; } ``` +
+ +## Windows 10 - Toast Configuration + +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. +The system configuration help you to define how long you want notifications to appear for (5 seconds to 5 minutes) as turning on visual notifications for sound. +![Ease of Access configuration](https://camo.githubusercontent.com/56c8edd1a7a4a43be07ba211d9d828478fdbad39/68747470733a2f2f7777772e686f77746f6765656b2e636f6d2f77702d636f6e74656e742f75706c6f6164732f323031362f30332f656173655f6f665f6163636573732e706e67)