diff --git a/.gitignore b/.gitignore index 77487a9..242984e 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,3 @@ enc_temp_folder/ build/ cmake-build-* CMakeLists.txt.user - -# CLion -.idea/ diff --git a/.idea/copyright/WinToast.xml b/.idea/copyright/WinToast.xml new file mode 100644 index 0000000..d16d728 --- /dev/null +++ b/.idea/copyright/WinToast.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..17ab555 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 35b5888..80c25c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.4...3.27) -project(wintoastlib VERSION 1.3.2 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 11) +project(wintoastlib VERSION 2.0.0 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -8,11 +8,11 @@ option(WINTOASTLIB_BUILD_EXAMPLES "Compile the examples" ON) option(WINTOASTLIB_QT_ENABLED "Enable Qt support to build the GUI examples" OFF) set(WINTOASTLIB_LIBNAME WinToast) -set(WINTOASTLIB_HEADERS ${CMAKE_CURRENT_LIST_DIR}/include/wintoastlib.h) -set(WINTOASTLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/wintoastlib.cpp) +file(GLOB_RECURSE WINTOASTLIB_HEADERS "${CMAKE_CURRENT_LIST_DIR}/include/*.h") +file(GLOB_RECURSE WINTOASTLIB_SOURCES "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp") add_library(${WINTOASTLIB_LIBNAME} STATIC ${WINTOASTLIB_HEADERS} ${WINTOASTLIB_SOURCES}) target_include_directories(${WINTOASTLIB_LIBNAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) -target_link_libraries(${WINTOASTLIB_LIBNAME} psapi) +target_link_libraries(${WINTOASTLIB_LIBNAME} psapi shlwapi user32) if (${WINTOASTLIB_BUILD_EXAMPLES}) add_subdirectory(examples) diff --git a/LICENSE.txt b/LICENSE.txt index 0182078..7784ae9 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui +Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/examples/console-example/main.cpp b/examples/console-example/main.cpp index 1dbdaed..692a41c 100644 --- a/examples/console-example/main.cpp +++ b/examples/console-example/main.cpp @@ -1,6 +1,7 @@ #include "wintoastlib.h" + #include -#include +#include using namespace WinToastLib; @@ -21,17 +22,17 @@ class CustomHandler : public IWinToastHandler { exit(0); } - void toastDismissed(WinToastDismissalReason state) const { + void toastDismissed(DismissalReason state) const { switch (state) { - case UserCanceled: + case DismissalReason::UserCanceled: std::wcout << L"The user dismissed this toast" << std::endl; exit(1); break; - case TimedOut: + case DismissalReason::TimedOut: std::wcout << L"The toast has timed out" << std::endl; exit(2); break; - case ApplicationHidden: + case DismissalReason::ApplicationHidden: std::wcout << L"The application hid the toast using ToastNotifier.hide()" << std::endl; exit(3); break; @@ -153,8 +154,8 @@ int wmain(int argc, LPWSTR* argv) { std::wcerr << L"--only-create-shortcut does not accept images/text/actions/expiration" << std::endl; return 9; } - enum WinToast::ShortcutResult result = WinToast::instance()->createShortcut(); - return result ? 16 + result : 0; + WinToast::ShortcutResult result = WinToast::instance()->createShortcut(); + return result != WinToast::ShortcutResult::SHORTCUT_UNCHANGED ? 16 + static_cast(result) : 0; } if (text.empty()) { @@ -166,8 +167,8 @@ int wmain(int argc, LPWSTR* argv) { return Results::InitializationFailure; } - WinToastTemplate templ(!imagePath.empty() ? WinToastTemplate::ImageAndText02 : WinToastTemplate::Text02); - templ.setTextField(text, WinToastTemplate::FirstLine); + WinToastTemplate templ(!imagePath.empty() ? WinToastTemplate::Type::ImageAndText02 : WinToastTemplate::Type::Text02); + templ.setTextField(text, WinToastTemplate::TextField::FirstLine); templ.setAudioOption(audioOption); templ.setAttributionText(attribute); templ.setImagePath(imagePath); diff --git a/examples/qt-gui-example/mainwindow.cpp b/examples/qt-gui-example/mainwindow.cpp index 58495b2..10a9c16 100644 --- a/examples/qt-gui-example/mainwindow.cpp +++ b/examples/qt-gui-example/mainwindow.cpp @@ -1,5 +1,8 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "wintoastlib.h" +#include "wintoastlib.h" + #include #include #include @@ -11,14 +14,14 @@ using namespace WinToastLib; MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); - ui->toastType->addItem("ImageAndText01", WinToastTemplate::ImageAndText01); - ui->toastType->addItem("ImageAndText02", WinToastTemplate::ImageAndText02); - ui->toastType->addItem("ImageAndText03", WinToastTemplate::ImageAndText03); - ui->toastType->addItem("ImageAndText04", WinToastTemplate::ImageAndText04); - ui->toastType->addItem("Text01", WinToastTemplate::Text01); - ui->toastType->addItem("Text02", WinToastTemplate::Text02); - ui->toastType->addItem("Text03", WinToastTemplate::Text03); - ui->toastType->addItem("Text04", WinToastTemplate::Text04); + ui->toastType->addItem("ImageAndText01", WinToastTemplate::Type::ImageAndText01); + ui->toastType->addItem("ImageAndText02", WinToastTemplate::Type::ImageAndText02); + ui->toastType->addItem("ImageAndText03", WinToastTemplate::Type::ImageAndText03); + ui->toastType->addItem("ImageAndText04", WinToastTemplate::Type::ImageAndText04); + ui->toastType->addItem("Text01", WinToastTemplate::Type::Text01); + ui->toastType->addItem("Text02", WinToastTemplate::Type::Text02); + ui->toastType->addItem("Text03", WinToastTemplate::Type::Text03); + ui->toastType->addItem("Text04", WinToastTemplate::Type::Text04); ui->audioMode->addItem("Default", WinToastTemplate::AudioOption::Default); ui->audioMode->addItem("Loop", WinToastTemplate::AudioOption::Loop); @@ -77,15 +80,15 @@ class CustomHandler : public IWinToastHandler { std::wcout << L"Error showing current toast" << std::endl; } - void toastDismissed(WinToastDismissalReason state) const { + void toastDismissed(DismissalReason state) const { switch (state) { - case UserCanceled: + case DismissalReason::UserCanceled: std::wcout << L"The user dismissed this toast" << std::endl; break; - case ApplicationHidden: + case DismissalReason::ApplicationHidden: std::wcout << L"The application hid the toast using ToastNotifier.hide()" << std::endl; break; - case TimedOut: + case DismissalReason::TimedOut: std::wcout << L"The toast has timed out" << std::endl; break; default: @@ -96,13 +99,13 @@ class CustomHandler : public IWinToastHandler { }; void MainWindow::on_showToast_clicked() { - auto const type = static_cast(ui->toastType->currentData().toInt()); + auto const type = static_cast(ui->toastType->currentData().toInt()); WinToastTemplate templ = WinToastTemplate(type); templ.setImagePath(ui->imagePath->text().toStdWString(), static_cast(ui->cropHint->currentData().toInt())); templ.setHeroImagePath(ui->heroPath->text().toStdWString(), ui->inlineHeroImage->isChecked()); - templ.setTextField(ui->firstLine->text().toStdWString(), WinToastTemplate::FirstLine); - templ.setTextField(ui->secondLine->text().toStdWString(), WinToastTemplate::SecondLine); - templ.setTextField(ui->thirdLine->text().toStdWString(), WinToastTemplate::ThirdLine); + templ.setTextField(ui->firstLine->text().toStdWString(), WinToastTemplate::TextField::FirstLine); + templ.setTextField(ui->secondLine->text().toStdWString(), WinToastTemplate::TextField::SecondLine); + templ.setTextField(ui->thirdLine->text().toStdWString(), WinToastTemplate::TextField::ThirdLine); templ.setExpiration(ui->spinBox->value() * 1000); templ.setAudioPath(static_cast(ui->audioSystemFile->currentData().toInt())); templ.setAudioOption(static_cast(ui->audioMode->currentData().toInt())); diff --git a/include/IWinToastHandler.h b/include/IWinToastHandler.h new file mode 100644 index 0000000..4971024 --- /dev/null +++ b/include/IWinToastHandler.h @@ -0,0 +1,49 @@ +// MIT License +// +// Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef IWINTOASTHANDLER_H +#define IWINTOASTHANDLER_H + +#include + +#include "Platform.h" + +namespace WinToastLib { + using namespace ABI::Windows::UI::Notifications; + + struct IWinToastHandler { + enum class DismissalReason : int32_t { + UserCanceled = ToastDismissalReason_UserCanceled, + ApplicationHidden = ToastDismissalReason_ApplicationHidden, + TimedOut = ToastDismissalReason_TimedOut + }; + + virtual ~IWinToastHandler() = default; + virtual void toastActivated() const = 0; + virtual void toastActivated(int actionIndex) const = 0; + virtual void toastActivated(std::wstring response) const = 0; + virtual void toastDismissed(DismissalReason state) const = 0; + virtual void toastFailed() const = 0; + }; +} + +#endif //IWINTOASTHANDLER_H \ No newline at end of file diff --git a/include/Platform.h b/include/Platform.h new file mode 100644 index 0000000..259143c --- /dev/null +++ b/include/Platform.h @@ -0,0 +1,43 @@ +// MIT License +// +// Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif //WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +namespace WinToastLib { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Data::Xml::Dom; + using namespace ABI::Windows::Foundation; + using namespace ABI::Windows::UI::Notifications; + using namespace Windows::Foundation; +} + +#endif //PLATFORM_H \ No newline at end of file diff --git a/include/WinToastTemplate.h b/include/WinToastTemplate.h new file mode 100644 index 0000000..ad3f8fc --- /dev/null +++ b/include/WinToastTemplate.h @@ -0,0 +1,143 @@ +// MIT License +// +// Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef WINTOASTTEMPLATE_H +#define WINTOASTTEMPLATE_H + +#include +#include +#include + +#include "Platform.h" + +namespace WinToastLib { + struct WinToastTemplate final { + enum class Scenario : int32_t { Default, Alarm, IncomingCall, Reminder }; + enum class Duration : int32_t { System, Short, Long }; + enum class AudioOption : int32_t { Default, Silent, Loop }; + enum class TextField : int32_t { FirstLine, SecondLine, ThirdLine }; + + enum class Type : int32_t { + ImageAndText01 = ToastTemplateType_ToastImageAndText01, + ImageAndText02 = ToastTemplateType_ToastImageAndText02, + ImageAndText03 = ToastTemplateType_ToastImageAndText03, + ImageAndText04 = ToastTemplateType_ToastImageAndText04, + Text01 = ToastTemplateType_ToastText01, + Text02 = ToastTemplateType_ToastText02, + Text03 = ToastTemplateType_ToastText03, + Text04 = ToastTemplateType_ToastText04 + }; + + enum class AudioSystemFile : int32_t { + DefaultSound, + IM, + Mail, + Reminder, + SMS, + Alarm, + Alarm2, + Alarm3, + Alarm4, + Alarm5, + Alarm6, + Alarm7, + Alarm8, + Alarm9, + Alarm10, + Call, + Call1, + Call2, + Call3, + Call4, + Call5, + Call6, + Call7, + Call8, + Call9, + Call10, + }; + + enum class CropHint : int32_t { + Square, + Circle, + }; + + WinToastTemplate(_In_ Type type = Type::ImageAndText02); + ~WinToastTemplate(); + + void setFirstLine(_In_ std::wstring const& text); + void setSecondLine(_In_ std::wstring const& text); + void setThirdLine(_In_ std::wstring const& text); + void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); + void setAttributionText(_In_ std::wstring const& attributionText); + void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); + void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); + void setAudioPath(_In_ AudioSystemFile audio); + void setAudioPath(_In_ std::wstring const& audioPath); + void setAudioOption(_In_ AudioOption audioOption); + void setDuration(_In_ Duration duration); + void setExpiration(_In_ INT64 millisecondsFromNow); + void setScenario(_In_ Scenario scenario); + void addAction(_In_ std::wstring const& label); + void addInput(); + + std::size_t textFieldsCount() const; + std::size_t actionsCount() const; + bool hasImage() const; + bool hasHeroImage() const; + std::vector const& textFields() const; + std::wstring const& textField(_In_ TextField pos) const; + std::wstring const& actionLabel(_In_ std::size_t pos) const; + std::wstring const& imagePath() const; + std::wstring const& heroImagePath() const; + std::wstring const& audioPath() const; + std::wstring const& attributionText() const; + std::wstring const& scenario() const; + INT64 expiration() const; + Type type() const; + AudioOption audioOption() const; + Duration duration() const; + bool isToastGeneric() const; + bool isInlineHeroImage() const; + bool isCropHintCircle() const; + bool isInput() const; + + private: + bool _hasInput{false}; + + std::vector _textFields{}; + std::vector _actions{}; + std::wstring _imagePath{}; + std::wstring _heroImagePath{}; + bool _inlineHeroImage{false}; + std::wstring _audioPath{}; + std::wstring _attributionText{}; + std::wstring _scenario{L"Default"}; + INT64 _expiration{0}; + AudioOption _audioOption{AudioOption::Default}; + Type _type{Type::Text01}; + Duration _duration{Duration::System}; + CropHint _cropHint{CropHint::Square}; + }; +} + +#endif //WINTOASTTEMPLATE_H \ No newline at end of file diff --git a/include/wintoastlib.h b/include/wintoastlib.h index aa7a88a..909a485 100644 --- a/include/wintoastlib.h +++ b/include/wintoastlib.h @@ -1,189 +1,42 @@ -/** - * MIT License - * - * Copyright (C) 2016-2025 WinToast v1.3.2 - Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ +// MIT License +// +// Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #ifndef WINTOASTLIB_H #define WINTOASTLIB_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include -using namespace Microsoft::WRL; -using namespace ABI::Windows::Data::Xml::Dom; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::UI::Notifications; -using namespace Windows::Foundation; +#include "Platform.h" +#include "IWinToastHandler.h" +#include "WinToastTemplate.h" namespace WinToastLib { - void setDebugOutputEnabled(bool enabled); - class IWinToastHandler { - public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, - ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut - }; - - virtual ~IWinToastHandler() = default; - virtual void toastActivated() const = 0; - virtual void toastActivated(int actionIndex) const = 0; - virtual void toastActivated(std::wstring response) const = 0; - virtual void toastDismissed(WinToastDismissalReason state) const = 0; - virtual void toastFailed() const = 0; - }; - - class WinToastTemplate { - public: - enum class Scenario { Default, Alarm, IncomingCall, Reminder }; - enum Duration { System, Short, Long }; - enum AudioOption { Default = 0, Silent, Loop }; - enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - - enum WinToastTemplateType { - ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, - ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, - ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, - ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, - Text01 = ToastTemplateType::ToastTemplateType_ToastText01, - Text02 = ToastTemplateType::ToastTemplateType_ToastText02, - Text03 = ToastTemplateType::ToastTemplateType_ToastText03, - Text04 = ToastTemplateType::ToastTemplateType_ToastText04 - }; - - enum AudioSystemFile { - DefaultSound, - IM, - Mail, - Reminder, - SMS, - Alarm, - Alarm2, - Alarm3, - Alarm4, - Alarm5, - Alarm6, - Alarm7, - Alarm8, - Alarm9, - Alarm10, - Call, - Call1, - Call2, - Call3, - Call4, - Call5, - Call6, - Call7, - Call8, - Call9, - Call10, - }; - - enum CropHint { - Square, - Circle, - }; - - WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); - ~WinToastTemplate(); - - void setFirstLine(_In_ std::wstring const& text); - void setSecondLine(_In_ std::wstring const& text); - void setThirdLine(_In_ std::wstring const& text); - void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); - void setAttributionText(_In_ std::wstring const& attributionText); - void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); - void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); - void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); - void setAudioPath(_In_ std::wstring const& audioPath); - void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); - void setDuration(_In_ Duration duration); - void setExpiration(_In_ INT64 millisecondsFromNow); - void setScenario(_In_ Scenario scenario); - void addAction(_In_ std::wstring const& label); - void addInput(); - - std::size_t textFieldsCount() const; - std::size_t actionsCount() const; - bool hasImage() const; - bool hasHeroImage() const; - std::vector const& textFields() const; - std::wstring const& textField(_In_ TextField pos) const; - std::wstring const& actionLabel(_In_ std::size_t pos) const; - std::wstring const& imagePath() const; - std::wstring const& heroImagePath() const; - std::wstring const& audioPath() const; - std::wstring const& attributionText() const; - std::wstring const& scenario() const; - INT64 expiration() const; - WinToastTemplateType type() const; - WinToastTemplate::AudioOption audioOption() const; - Duration duration() const; - bool isToastGeneric() const; - bool isInlineHeroImage() const; - bool isCropHintCircle() const; - bool isInput() const; - - private: - bool _hasInput{false}; - - std::vector _textFields{}; - std::vector _actions{}; - std::wstring _imagePath{}; - std::wstring _heroImagePath{}; - bool _inlineHeroImage{false}; - std::wstring _audioPath{}; - std::wstring _attributionText{}; - std::wstring _scenario{L"Default"}; - INT64 _expiration{0}; - AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; - WinToastTemplateType _type{WinToastTemplateType::Text01}; - Duration _duration{Duration::System}; - CropHint _cropHint{CropHint::Square}; - }; - class WinToast { public: - enum WinToastError { - NoError = 0, + enum class WinToastError : int32_t { + NoError, NotInitialized, SystemNotSupported, ShellLinkNotCreated, @@ -194,53 +47,52 @@ namespace WinToastLib { UnknownError }; - enum ShortcutResult { + enum class ShortcutResult : int32_t { 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 }; - enum ShortcutPolicy { + enum class ShortcutPolicy : int32_t { /* Don't check, create, or modify a shortcut. */ - SHORTCUT_POLICY_IGNORE = 0, + SHORTCUT_POLICY_IGNORE, /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ - SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, + SHORTCUT_POLICY_REQUIRE_NO_CREATE, /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ - SHORTCUT_POLICY_REQUIRE_CREATE = 2, + SHORTCUT_POLICY_REQUIRE_CREATE, }; - WinToast(void); + WinToast(); virtual ~WinToast(); - static WinToast* instance(); - static bool isCompatible(); - static bool isSupportingModernFeatures(); - static bool isWin10AnniversaryOrHigher(); - static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, + [[nodiscard]] static WinToast* instance(); + [[nodiscard]] static bool isCompatible(); + [[nodiscard]] static bool isSupportingModernFeatures(); + [[nodiscard]] static bool isWin10AnniversaryOrHigher(); + [[nodiscard]] static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, _In_ std::wstring const& subProduct = std::wstring(), _In_ std::wstring const& versionInformation = std::wstring()); - static std::wstring const& strerror(_In_ WinToastError error); - virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); - virtual bool isInitialized() const; - virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, + [[nodiscard]] static std::wstring const& strerror(_In_ WinToastError error); + bool initialize(_Out_opt_ WinToastError* error = nullptr); + [[nodiscard]] bool isInitialized() const; + bool hideToast(_In_ INT64 id); + INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_opt_ WinToastError* error = nullptr); - virtual void clear(); - virtual enum ShortcutResult createShortcut(); + void clear(); + [[nodiscard]] ShortcutResult createShortcut(); - std::wstring const& appName() const; - std::wstring const& appUserModelId() const; + [[nodiscard]] std::wstring const& appName() const; + [[nodiscard]] std::wstring const& appUserModelId() const; void setAppUserModelId(_In_ std::wstring const& aumi); void setAppName(_In_ std::wstring const& appName); void setShortcutPolicy(_In_ ShortcutPolicy policy); protected: struct NotifyData { - NotifyData() {}; + NotifyData() {} NotifyData(_In_ ComPtr notify, _In_ EventRegistrationToken activatedToken, _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} @@ -272,11 +124,11 @@ namespace WinToastLib { _readyForDeletion = true; } - bool isReadyForDeletion() const { + [[nodiscard]] bool isReadyForDeletion() const { return _readyForDeletion; } - IToastNotification* notification() { + [[nodiscard]] IToastNotification* notification() { return _notify.Get(); } @@ -290,28 +142,26 @@ namespace WinToastLib { }; bool _isInitialized{false}; - bool _hasCoInitialized{false}; - ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; + ShortcutPolicy _shortcutPolicy{ShortcutPolicy::SHORTCUT_POLICY_REQUIRE_CREATE}; std::wstring _appName{}; std::wstring _aumi{}; std::map _buffer{}; void markAsReadyForDeletion(_In_ INT64 id); - HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); - HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); - HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); - HRESULT - setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, + [[nodiscard]] HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); + [[nodiscard]] HRESULT createShellLinkHelper(); + [[nodiscard]] HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); + [[nodiscard]] HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); + [[nodiscard]] HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); + [[nodiscard]] HRESULT setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); - HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); - HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); - HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); - HRESULT addInputHelper(_In_ IXmlDocument* xml); - ComPtr notifier(_In_ bool* succeded) const; + [[nodiscard]] HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); + [[nodiscard]] HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); + [[nodiscard]] HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); + [[nodiscard]] HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); + [[nodiscard]] HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); + [[nodiscard]] HRESULT addInputHelper(_In_ IXmlDocument* xml); + [[nodiscard]] ComPtr notifier(_In_ bool* succeded) const; void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); }; } // namespace WinToastLib diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index 2de25e8..deec6d9 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -1,40 +1,51 @@ -/** - * MIT License - * - * Copyright (C) 2016-2025 WinToast v1.3.2 - Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ +// MIT License +// +// Copyright (C) 2016-2025 WinToast - Mohammed Boujemaoui +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #include "wintoastlib.h" #include -#include #include #include #include - -#pragma comment(lib, "shlwapi") -#pragma comment(lib, "user32") - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -#define STATUS_SUCCESS (0x00000000) +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr std::wstring_view DEFAULT_SHELL_LINKS_PATH = L"\\Microsoft\\Windows\\Start Menu\\Programs\\"; +constexpr std::wstring_view DEFAULT_LINK_FORMAT = L".lnk"; +constexpr NTSTATUS STATUS_SUCCESS = 0x00000000; #ifdef NDEBUG static bool DebugOutputEnabled = false; @@ -69,15 +80,15 @@ namespace DllImporter { return (func != nullptr) ? S_OK : E_FAIL; } - typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, + typedef HRESULT(STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); + typedef HRESULT(STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); + typedef HRESULT(STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void** factory); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, + typedef HRESULT(STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER* hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); - typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); + typedef PCWSTR(STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); + typedef HRESULT(STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; static f_PropVariantToString PropVariantToString; @@ -87,12 +98,12 @@ namespace DllImporter { static f_WindowsDeleteString WindowsDeleteString; template - __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); } template - inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { + HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); } @@ -122,7 +133,7 @@ class WinToastStringWrapper { WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) { HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), 0, 0, nullptr); + RaiseException(STATUS_INVALID_PARAMETER, 0, 0, nullptr); } } @@ -130,7 +141,7 @@ class WinToastStringWrapper { HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), 0, 0, nullptr); + RaiseException(STATUS_INVALID_PARAMETER, 0, 0, nullptr); } } @@ -138,7 +149,7 @@ class WinToastStringWrapper { DllImporter::WindowsDeleteString(_hstring); } - inline HSTRING Get() const noexcept { + HSTRING Get() const noexcept { return _hstring; } @@ -227,42 +238,36 @@ namespace Util { return rovi; } - inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); - DEBUG_MSG("Default executable path: " << path); - return (written > 0) ? S_OK : E_FAIL; - } - - inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link path: " << path); + inline std::optional defaultExecutablePath() { + std::array buffer{}; + const auto written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, buffer.data(), MAX_PATH); + if (written <= 0) { + return std::nullopt; } - return hr; + std::wstring path{buffer.data(), written}; + DEBUG_MSG("Default executable path: " << path); + return std::filesystem::path{std::move(path)}; } - inline HRESULT defaultShellLinkPath(_In_ std::wstring const& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - std::wstring const appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link file path: " << path); + inline std::optional defaultShellLinksDirectory() { + std::array buffer{}; + const auto written = GetEnvironmentVariableW(L"APPDATA", buffer.data(), MAX_PATH); + if (written <= 0) { + return std::nullopt; } - return hr; + std::wstring path{buffer.data(), written}; + DEBUG_MSG("Default shell link path: " << path); + return std::filesystem::path{std::move(path)}; } - inline std::wstring parentDirectory(WCHAR* path, DWORD size) { - size_t lastSeparator = 0; - for (size_t i = 0; i < size; i++) { - if (path[i] == L'\\' || path[i] == L'/') { - lastSeparator = i; - } + inline std::optional defaultShellLinkPath(const std::wstring& appname) { + const auto path = defaultShellLinksDirectory(); + if (!path) { + return std::nullopt; } - return {path, lastSeparator}; + std::wstringstream appLink; + appLink << appname << DEFAULT_LINK_FORMAT; + return *path / appLink.str(); } inline PCWSTR AsString(_In_ ComPtr& xmlDocument) { @@ -295,7 +300,7 @@ namespace Util { } template - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, + HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, _In_ FunctorT&& markAsReadyForDeletionFunc) { @@ -350,7 +355,7 @@ namespace Util { } if (arguments && *arguments) { - eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); + eventHandler->toastActivated(wcstol(arguments, nullptr, 10)); DllImporter::WindowsDeleteString(argumentsHandle); markAsReadyForDeletionFunc(); return S_OK; @@ -375,7 +380,7 @@ namespace Util { InternalDateTime::Now() >= expirationTime) { reason = ToastDismissalReason_TimedOut; } - eventHandler->toastDismissed(static_cast(reason)); + eventHandler->toastDismissed(static_cast(reason)); } markAsReadyForDeletionFunc(); return S_OK; @@ -398,7 +403,7 @@ namespace Util { } inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { - ComPtr srcAttribute; + ComPtr srcAttribute; HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); if (SUCCEEDED(hr)) { ComPtr node; @@ -419,7 +424,7 @@ namespace Util { ComPtr root; hr = rootList->Item(0, &root); if (SUCCEEDED(hr)) { - ComPtr audioElement; + ComPtr audioElement; hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); if (SUCCEEDED(hr)) { ComPtr audioNodeTmp; @@ -445,20 +450,20 @@ namespace Util { } // namespace Util WinToast* WinToast::instance() { - thread_local static WinToast instance; + static thread_local WinToast instance; return &instance; } -WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { +WinToast::WinToast() { if (!isCompatible()) { - DEBUG_MSG(L"Warning: Your system is not compatible with this library "); + DEBUG_MSG(L"Warning: Your system is not compatible with this library"); + return; } } WinToast::~WinToast() { clear(); - - if (_hasCoInitialized) { + if (_isInitialized) { CoUninitialize(); } } @@ -483,12 +488,12 @@ bool WinToast::isCompatible() { (DllImporter::WindowsDeleteString == nullptr)); } -bool WinToastLib::WinToast::isSupportingModernFeatures() { +bool WinToast::isSupportingModernFeatures() { constexpr auto MinimumSupportedVersion = 6; return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; } -bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { +bool WinToast::isWin10AnniversaryOrHigher() { return Util::getRealOSVersion().dwBuildNumber >= 14393; } @@ -509,7 +514,7 @@ std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ return aumi; } -std::wstring const& WinToast::strerror(WinToastError error) { +[[nodiscard]] std::wstring const& WinToast::strerror(WinToastError error) { static std::unordered_map const Labels = { {WinToastError::NoError, L"No error. The process was executed correctly" }, {WinToastError::NotInitialized, L"The library has not been initialized" }, @@ -526,42 +531,35 @@ std::wstring const& WinToast::strerror(WinToastError error) { return iter->second; } -enum WinToast::ShortcutResult WinToast::createShortcut() { +[[nodiscard]] WinToast::ShortcutResult WinToast::createShortcut() { if (_aumi.empty() || _appName.empty()) { DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); - return SHORTCUT_MISSING_PARAMETERS; + return ShortcutResult::SHORTCUT_MISSING_PARAMETERS; } if (!isCompatible()) { DEBUG_MSG(L"Your OS is not compatible with this library! =("); - return SHORTCUT_INCOMPATIBLE_OS; - } - - if (!_hasCoInitialized) { - HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); - if (initHr != RPC_E_CHANGED_MODE) { - if (FAILED(initHr) && initHr != S_FALSE) { - DEBUG_MSG(L"Error on COM library initialization!"); - return SHORTCUT_COM_INIT_FAILURE; - } else { - _hasCoInitialized = true; - } - } + return ShortcutResult::SHORTCUT_INCOMPATIBLE_OS; } bool wasChanged; HRESULT hr = validateShellLinkHelper(wasChanged); if (SUCCEEDED(hr)) { - return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; + return wasChanged ? ShortcutResult::SHORTCUT_WAS_CHANGED : ShortcutResult::SHORTCUT_UNCHANGED; } hr = createShellLinkHelper(); - return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; + return SUCCEEDED(hr) ? ShortcutResult::SHORTCUT_WAS_CREATED : ShortcutResult::SHORTCUT_CREATE_FAILED; } bool WinToast::initialize(_Out_opt_ WinToastError* error) { - _isInitialized = false; - setError(error, WinToastError::NoError); + HRESULT initHr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (initHr != RPC_E_CHANGED_MODE) { + if (FAILED(initHr) && initHr != S_FALSE) { + DEBUG_MSG(L"Error on COM library initialization!"); + return false; + } + } if (!isCompatible()) { setError(error, WinToastError::SystemNotSupported); @@ -575,8 +573,8 @@ bool WinToast::initialize(_Out_opt_ WinToastError* error) { return false; } - if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) { - if (createShortcut() < 0) { + if (_shortcutPolicy != ShortcutPolicy::SHORTCUT_POLICY_IGNORE) { + if (createShortcut() < ShortcutResult::SHORTCUT_UNCHANGED) { setError(error, WinToastError::ShellLinkNotCreated); DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); return false; @@ -593,25 +591,27 @@ bool WinToast::initialize(_Out_opt_ WinToastError* error) { return _isInitialized; } -bool WinToast::isInitialized() const { +[[nodiscard]] bool WinToast::isInitialized() const { return _isInitialized; } -std::wstring const& WinToast::appName() const { +[[nodiscard]] std::wstring const& WinToast::appName() const { return _appName; } -std::wstring const& WinToast::appUserModelId() const { +[[nodiscard]] std::wstring const& WinToast::appUserModelId() const { return _aumi; } -HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { - WCHAR path[MAX_PATH] = {L'\0'}; - Util::defaultShellLinkPath(_appName, path); +[[nodiscard]] HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { + const auto path = Util::defaultShellLinkPath(_appName); + if (!path) { + return E_FAIL; + } // Check if the file exist - DWORD attr = GetFileAttributesW(path); + const auto attr = GetFileAttributesW(path->c_str()); if (attr >= 0xFFFFFFF) { - DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); + DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path->wstring()); return E_FAIL; } @@ -627,7 +627,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { ComPtr persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { - hr = persistFile->Load(path, STGM_READWRITE); + hr = persistFile->Load(path->c_str(), STGM_READWRITE); if (SUCCEEDED(hr)) { ComPtr propertyStore; hr = shellLink.As(&propertyStore); @@ -639,7 +639,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); wasChanged = false; if (FAILED(hr) || _aumi != AUMI) { - if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { + if (_shortcutPolicy == ShortcutPolicy::SHORTCUT_POLICY_REQUIRE_CREATE) { // AUMI Changed for the same app, let's update the current value! =) wasChanged = true; PropVariantClear(&appIdPropVar); @@ -649,7 +649,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(path, TRUE); + hr = persistFile->Save(path->c_str(), TRUE); } } } @@ -667,20 +667,23 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { return hr; } -HRESULT WinToast::createShellLinkHelper() { - if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { +[[nodiscard]] HRESULT WinToast::createShellLinkHelper() { + if (_shortcutPolicy != ShortcutPolicy::SHORTCUT_POLICY_REQUIRE_CREATE) { return E_FAIL; } - - WCHAR exePath[MAX_PATH]{L'\0'}; - WCHAR slPath[MAX_PATH]{L'\0'}; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - std::wstring exeDir = Util::parentDirectory(exePath, sizeof(exePath) / sizeof(exePath[0])); + const auto slPath = Util::defaultShellLinkPath(_appName); + if (!slPath) { + return E_FAIL; + } + const auto exePath = Util::defaultExecutablePath(); + if (!exePath) { + return E_FAIL; + } + const auto exeDir = exePath->parent_path(); ComPtr shellLink; HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); + hr = shellLink->SetPath(exePath->c_str()); if (SUCCEEDED(hr)) { hr = shellLink->SetArguments(L""); if (SUCCEEDED(hr)) { @@ -699,7 +702,7 @@ HRESULT WinToast::createShellLinkHelper() { ComPtr persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); + hr = persistFile->Save(slPath->c_str(), TRUE); } } } @@ -717,7 +720,7 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand std::shared_ptr handler(eventHandler); setError(error, WinToastError::NoError); INT64 id = -1; - if (!isInitialized()) { + if (!_isInitialized) { setError(error, WinToastError::NotInitialized); DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); return id; @@ -839,7 +842,7 @@ INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHand return FAILED(hr) ? -1 : id; } -ComPtr WinToast::notifier(_In_ bool* succeded) const { +[[nodiscard]] ComPtr WinToast::notifier(_In_ bool* succeded) const { ComPtr notificationManager; ComPtr notifier; HRESULT hr = DllImporter::Wrap_GetActivationFactory( @@ -870,7 +873,7 @@ void WinToast::markAsReadyForDeletion(_In_ INT64 id) { } bool WinToast::hideToast(_In_ INT64 id) { - if (!isInitialized()) { + if (!_isInitialized) { DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); return false; } @@ -899,6 +902,10 @@ bool WinToast::hideToast(_In_ INT64 id) { } void WinToast::clear() { + if (!_isInitialized) { + DEBUG_MSG("Could not clear notifications, library not initialized"); + return; + } auto succeded = false; auto notify = notifier(&succeded); if (!succeded) { @@ -921,7 +928,7 @@ void WinToast::clear() { // NOTE: This will add a new text field, so be aware when iterating over // the toast's text fields or getting a count of them. // -HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { +[[nodiscard]] HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { Util::createElement(xml, L"binding", L"text", {L"placement"}); ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); @@ -955,7 +962,7 @@ HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std return hr; } -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { +[[nodiscard]] HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -976,7 +983,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring co return hr; } -HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { +[[nodiscard]] HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -997,7 +1004,7 @@ HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring co return hr; } -HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) { +[[nodiscard]] HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) { std::vector attrbs; attrbs.push_back(L"id"); attrbs.push_back(L"type"); @@ -1046,7 +1053,7 @@ HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) { return hr; } -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { +[[nodiscard]] HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -1059,7 +1066,7 @@ HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring c return hr; } -HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { +[[nodiscard]] HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -1080,7 +1087,7 @@ HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { return hr; } -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, +[[nodiscard]] HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, _In_ bool isCropHintCircle) { assert(path.size() < MAX_PATH); @@ -1117,7 +1124,7 @@ HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring return hr; } -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, +[[nodiscard]] HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_opt_ WinToastTemplate::AudioOption option) { std::vector attrs; if (!path.empty()) { @@ -1239,7 +1246,7 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring cons return hr; } -HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { +[[nodiscard]] HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -1279,16 +1286,16 @@ void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value } } -WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { +WinToastTemplate::WinToastTemplate(_In_ Type type) : _type(type) { constexpr static std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[type], L""); + _textFields = std::vector(TextFieldsCount[static_cast(type)], L""); } WinToastTemplate::~WinToastTemplate() { _textFields.clear(); } -void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { +void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ TextField pos) { auto const position = static_cast(pos); if (position >= _textFields.size()) { DEBUG_MSG("The selected template type supports only " << _textFields.size() << " text lines"); @@ -1345,20 +1352,20 @@ void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { _audioPath = iter->second; } -void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { +void WinToastTemplate::setAudioOption(_In_ AudioOption audioOption) { _audioOption = audioOption; } void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::FirstLine); + setTextField(text, TextField::FirstLine); } void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::SecondLine); + setTextField(text, TextField::SecondLine); } void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::ThirdLine); + setTextField(text, TextField::ThirdLine); } void WinToastTemplate::setDuration(_In_ Duration duration) { @@ -1369,7 +1376,7 @@ void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } -void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { +void WinToastTemplate::setScenario(_In_ Scenario scenario) { switch (scenario) { case Scenario::Default: _scenario = L"Default"; @@ -1407,7 +1414,7 @@ std::size_t WinToastTemplate::actionsCount() const { } bool WinToastTemplate::hasImage() const { - return _type < WinToastTemplateType::Text01; + return _type < Type::Text01; } bool WinToastTemplate::hasHeroImage() const { @@ -1445,7 +1452,7 @@ std::wstring const& WinToastTemplate::attributionText() const { return _attributionText; } -std::wstring const& WinToastLib::WinToastTemplate::scenario() const { +std::wstring const& WinToastTemplate::scenario() const { return _scenario; } @@ -1453,7 +1460,7 @@ INT64 WinToastTemplate::expiration() const { return _expiration; } -WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { +WinToastTemplate::Type WinToastTemplate::type() const { return _type; } @@ -1466,7 +1473,7 @@ WinToastTemplate::Duration WinToastTemplate::duration() const { } bool WinToastTemplate::isToastGeneric() const { - return hasHeroImage() || _cropHint == WinToastTemplate::Circle; + return hasHeroImage() || _cropHint == CropHint::Circle; } bool WinToastTemplate::isInlineHeroImage() const {