From 4375f2b2d9ca27ad26e5b2de0412394ccaf9a11f Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sat, 8 Apr 2023 13:03:06 +0200 Subject: [PATCH] Refactoring latest pull-request to add support to HeroImages and Crop Images --- .clang-format | 240 +++++++++++++++ .gitignore | 2 + CMakeLists.txt | 18 ++ include/wintoastlib.h | 307 +++++++++++++++++++ src/wintoastlib.cpp | 672 ++++++++++++++++++++---------------------- src/wintoastlib.h | 296 ------------------- 6 files changed, 885 insertions(+), 650 deletions(-) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 include/wintoastlib.h delete mode 100644 src/wintoastlib.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bea9bea --- /dev/null +++ b/.clang-format @@ -0,0 +1,240 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlinesLeft: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability + - __cold + - __hot + - __noinline + - __naked + - __noreturn + - __packed + - __pure + - __section + - __unused + - __used +BasedOnStyle: InheritParentConfig +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: Always +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +BreakInheritanceList: AfterColon +BreakStringLiterals: true +ColumnLimit: 140 +CommentPragmas: '' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: False +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - Q_FOREACH + - foreach + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 2 + CaseSensitive: True + - Regex: '^((<|")(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 2 + CaseSensitive: True + - Regex: '<[[:alnum:].]+>' + Priority: 4 + SortPriority: 4 + CaseSensitive: True + - Regex: '.*' + Priority: 1 + SortPriority: 1 + CaseSensitive: True +IncludeIsMainRegex: '(_test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: false +IndentWidth: 4 +IndentWrappedFunctionNames: true +InsertBraces: true +InsertTrailingCommas: None +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +Language: Cpp +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +NamespaceMacros: +PPIndentWidth: 4 +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +QualifierAlignment: Custom +QualifierOrder: ['inline', 'constexpr', 'static', 'type', 'const', 'volatile'] +RawStringFormats: + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: JavaScript + Delimiters: + - 'json' + - 'js' + EnclosingFunctions: + - 'json' + BasedOnStyle: google +ReferenceAlignment: Left +ReflowComments: false +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortUsingDeclarations: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: true + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +TypenameMacros: +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME diff --git a/.gitignore b/.gitignore index 617ec30..d2c011a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ Debug/ Release/ enc_temp_folder/ /example/qt-gui-example/build-WinToastExample-Desktop_Qt_5_9_2_MSVC2015_32bit-Debug +build/ +cmake-build-* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..835e2fb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.4) +project(wintoastlib VERSION 0.0.1 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +option(WINTOASTLIB_BUILD_EXAMPLES "If enabled, the examples will be built" 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) +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) + +if (WINTOASTLIB_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() \ No newline at end of file diff --git a/include/wintoastlib.h b/include/wintoastlib.h new file mode 100644 index 0000000..4041217 --- /dev/null +++ b/include/wintoastlib.h @@ -0,0 +1,307 @@ +/* * Copyright (C) 2016-2023 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 + +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; + +namespace WinToastLib { + + 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 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); + 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); + + 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; + + private: + 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, + 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 + }; + + enum ShortcutPolicy { + /* Don't check, create, or modify a shortcut. */ + SHORTCUT_POLICY_IGNORE = 0, + /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ + SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, + /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ + SHORTCUT_POLICY_REQUIRE_CREATE = 2, + }; + + WinToast(void); + 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, + _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, + _Out_opt_ WinToastError* error = nullptr); + virtual void clear(); + virtual enum ShortcutResult createShortcut(); + + std::wstring const& appName() const; + 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(_In_ ComPtr notify, _In_ EventRegistrationToken activatedToken, + _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : + _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} + + ~NotifyData() { + RemoveTokens(); + } + + void RemoveTokens() { + if (!_readyForDeletion) { + return; + } + + if (_previouslyTokenRemoved) { + return; + } + + if (!_notify.Get()) { + return; + } + + _notify->remove_Activated(_activatedToken); + _notify->remove_Dismissed(_dismissedToken); + _notify->remove_Failed(_failedToken); + _previouslyTokenRemoved = true; + } + + void markAsReadyForDeletion() { + _readyForDeletion = true; + } + + bool isReadyForDeletion() const { + return _readyForDeletion; + } + + IToastNotification* notification() { + return _notify.Get(); + } + + private: + ComPtr _notify{nullptr}; + EventRegistrationToken _activatedToken{}; + EventRegistrationToken _dismissedToken{}; + EventRegistrationToken _failedToken{}; + bool _readyForDeletion{false}; + bool _previouslyTokenRemoved{false}; + }; + + bool _isInitialized{false}; + bool _hasCoInitialized{false}; + 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, + _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); + ComPtr notifier(_In_ bool* succeded) const; + void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); + }; +} // namespace WinToastLib +#endif // WINTOASTLIB_H diff --git a/src/wintoastlib.cpp b/src/wintoastlib.cpp index 42e2a37..9956240 100644 --- a/src/wintoastlib.cpp +++ b/src/wintoastlib.cpp @@ -1,4 +1,4 @@ -/* * Copyright (C) 2016-2019 Mohammed Boujemaoui +/* * Copyright (C) 2016-2023 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 @@ -17,26 +17,32 @@ * 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") +#pragma comment(lib, "shlwapi") +#pragma comment(lib, "user32") #ifdef NDEBUG - #define DEBUG_MSG(str) do { } while ( false ) +# define DEBUG_MSG(str) \ + do { \ + } while (false) #else - #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false ) +# define DEBUG_MSG(str) \ + do { \ + std::wcout << str << std::endl; \ + } while (false) #endif -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -#define STATUS_SUCCESS (0x00000000) - +#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" +#define DEFAULT_LINK_FORMAT L".lnk" +#define STATUS_SUCCESS (0x00000000) // Quickstart: Handling toast activations from Win32 apps in Windows 10 // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ @@ -45,7 +51,7 @@ namespace DllImporter { // Function load a function from library template - HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { + HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function& func) { if (!library) { return E_INVALIDARG; } @@ -53,49 +59,53 @@ 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, _COM_Outptr_ void ** factory); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); - typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32 *length); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); - - static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; - static f_PropVariantToString PropVariantToString; - static f_RoGetActivationFactory RoGetActivationFactory; - static f_WindowsCreateStringReference WindowsCreateStringReference; - static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; - static f_WindowsDeleteString WindowsDeleteString; - - - template - _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + 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, + _COM_Outptr_ void** factory); + typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, + _Out_ HSTRING_HEADER* hstringHeader, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); + typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); + typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); + + static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; + static f_PropVariantToString PropVariantToString; + static f_RoGetActivationFactory RoGetActivationFactory; + static f_WindowsCreateStringReference WindowsCreateStringReference; + static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; + static f_WindowsDeleteString WindowsDeleteString; + + template + __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); } - template + template inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); } inline HRESULT initialize() { HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); - HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); + HRESULT hr = + loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); if (SUCCEEDED(hr)) { HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); + hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); if (SUCCEEDED(hr)) { HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); - const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); + bool const succeded = + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); return succeded ? S_OK : E_FAIL; } } return hr; } -} +} // namespace DllImporter class WinToastStringWrapper { public: @@ -106,8 +116,9 @@ class WinToastStringWrapper { } } - WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); + WinToastStringWrapper(_In_ std::wstring const& stringRef) noexcept { + HRESULT hr = + DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } @@ -120,10 +131,10 @@ class WinToastStringWrapper { inline HSTRING Get() const noexcept { return _hstring; } + private: HSTRING _hstring; HSTRING_HEADER _header; - }; class InternalDateTime : public IReference { @@ -131,7 +142,7 @@ class InternalDateTime : public IReference { static INT64 Now() { FILETIME now; GetSystemTimeAsFileTime(&now); - return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); + return ((((INT64) now.dwHighDateTime) << 32) | now.dwLowDateTime); } InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} @@ -146,7 +157,7 @@ class InternalDateTime : public IReference { return _dateTime.UniversalTime; } - HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) { + HRESULT STDMETHODCALLTYPE get_Value(DateTime* dateTime) { *dateTime = _dateTime; return S_OK; } @@ -195,14 +206,14 @@ namespace Util { if (hMod) { RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = { 0 }; + RTL_OSVERSIONINFOW rovi = {0}; rovi.dwOSVersionInfoSize = sizeof(rovi); if (STATUS_SUCCESS == fxPtr(&rovi)) { return rovi; } } } - RTL_OSVERSIONINFOW rovi = { 0 }; + RTL_OSVERSIONINFOW rovi = {0}; return rovi; } @@ -212,37 +223,36 @@ namespace Util { 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; + 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; + hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link path: " << path); } return hr; } - inline HRESULT defaultShellLinkPath(_In_ const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_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)) { const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; + hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link file path: " << path); } return hr; } - - inline PCWSTR AsString(_In_ ComPtr &xmlDocument) { + inline PCWSTR AsString(_In_ ComPtr& xmlDocument) { HSTRING xml; ComPtr ser; HRESULT hr = xmlDocument.As(&ser); - hr = ser->GetXml(&xml); - if (SUCCEEDED(hr)) + hr = ser->GetXml(&xml); + if (SUCCEEDED(hr)) { return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); + } return nullptr; } @@ -250,9 +260,9 @@ namespace Util { return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); } - inline HRESULT setNodeStringValue(_In_ const std::wstring& string, _Out_opt_ IXmlNode *node, _Out_ IXmlDocument *xml) { + inline HRESULT setNodeStringValue(_In_ std::wstring const& string, _Out_opt_ IXmlNode* node, _Out_ IXmlDocument* xml) { ComPtr textNode; - HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); + HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); if (SUCCEEDED(hr)) { ComPtr stringNode; hr = textNode.As(&stringNode); @@ -264,14 +274,13 @@ namespace Util { return hr; } - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime, - _Out_ EventRegistrationToken& activatedToken, _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, - std::function processForDeletionFunc) { + inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, + _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, + _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, + std::function processForDeletionFunc) { HRESULT hr = notification->add_Activated( - Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler, processForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) - { + Callback, ITypedEventHandler>>( + [eventHandler, processForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) { ComPtr activatedEventArgs; HRESULT hr = inspectable->QueryInterface(activatedEventArgs.GetAddressOf()); if (SUCCEEDED(hr)) { @@ -291,38 +300,43 @@ namespace Util { eventHandler->toastActivated(); processForDeletionFunc(); return S_OK; - }).Get(), &activatedToken); + }) + .Get(), + &activatedToken); if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler, expirationTime, processForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) - { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) - { - if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime) - reason = ToastDismissalReason_TimedOut; - eventHandler->toastDismissed(static_cast(reason)); - } - processForDeletionFunc(); - return S_OK; - }).Get(), &dismissedToken); + hr = notification->add_Dismissed( + Callback, ITypedEventHandler>>( + [eventHandler, expirationTime, processForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) { + ToastDismissalReason reason; + if (SUCCEEDED(e->get_Reason(&reason))) { + if (reason == ToastDismissalReason_UserCanceled && expirationTime && + InternalDateTime::Now() >= expirationTime) { + reason = ToastDismissalReason_TimedOut; + } + eventHandler->toastDismissed(static_cast(reason)); + } + processForDeletionFunc(); + return S_OK; + }) + .Get(), + &dismissedToken); if (SUCCEEDED(hr)) { - hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler, processForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) - { - eventHandler->toastFailed(); - processForDeletionFunc(); - return S_OK; - }).Get(), &failedToken); + hr = notification->add_Failed( + Callback, ITypedEventHandler>>( + [eventHandler, processForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) { + eventHandler->toastFailed(); + processForDeletionFunc(); + return S_OK; + }) + .Get(), + &failedToken); } } return hr; } - inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) { + inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { ComPtr srcAttribute; HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); if (SUCCEEDED(hr)) { @@ -336,7 +350,8 @@ namespace Util { return hr; } - inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector& attribute_names) { + inline HRESULT createElement(_In_ IXmlDocument* xml, _In_ std::wstring const& root_node, _In_ std::wstring const& element_name, + _In_ std::vector const& attribute_names) { ComPtr rootList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); if (SUCCEEDED(hr)) { @@ -355,7 +370,7 @@ namespace Util { ComPtr attributes; hr = audioNode->get_Attributes(&attributes); if (SUCCEEDED(hr)) { - for (const auto& it : attribute_names) { + for (auto const& it : attribute_names) { hr = addAttribute(xml, it, attributes.Get()); } } @@ -366,17 +381,14 @@ namespace Util { } return hr; } -} +} // namespace Util WinToast* WinToast::instance() { static WinToast instance; return &instance; } -WinToast::WinToast() : - _isInitialized(false), - _hasCoInitialized(false) -{ +WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { if (!isCompatible()) { DEBUG_MSG(L"Warning: Your system is not compatible with this library "); } @@ -390,12 +402,11 @@ WinToast::~WinToast() { } } -void WinToast::setAppName(_In_ const std::wstring& appName) { +void WinToast::setAppName(_In_ std::wstring const& appName) { _appName = appName; } - -void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { +void WinToast::setAppUserModelId(_In_ std::wstring const& aumi) { _aumi = aumi; DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); } @@ -406,28 +417,22 @@ void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { bool WinToast::isCompatible() { DllImporter::initialize(); - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) - || (DllImporter::PropVariantToString == nullptr) - || (DllImporter::RoGetActivationFactory == nullptr) - || (DllImporter::WindowsCreateStringReference == nullptr) - || (DllImporter::WindowsDeleteString == nullptr)); + return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || + (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || + (DllImporter::WindowsDeleteString == nullptr)); } bool WinToastLib::WinToast::isSupportingModernFeatures() { constexpr auto MinimumSupportedVersion = 6; return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; - } bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { return Util::getRealOSVersion().dwBuildNumber >= 14393; } -std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, - _In_ const std::wstring &productName, - _In_ const std::wstring &subProduct, - _In_ const std::wstring &versionInformation) -{ +std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, + _In_ std::wstring const& subProduct, _In_ std::wstring const& versionInformation) { std::wstring aumi = companyName; aumi += L"." + productName; if (subProduct.length() > 0) { @@ -443,19 +448,19 @@ std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, return aumi; } -const std::wstring& WinToast::strerror(WinToastError error) { +std::wstring const& WinToast::strerror(WinToastError error) { static const std::unordered_map Labels = { - {WinToastError::NoError, L"No error. The process was executed correctly"}, - {WinToastError::NotInitialized, L"The library has not been initialized"}, - {WinToastError::SystemNotSupported, L"The OS does not support WinToast"}, - {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"}, - {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"}, - {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"}, - {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, - {WinToastError::UnknownError, L"Unknown error"} + {WinToastError::NoError, L"No error. The process was executed correctly" }, + {WinToastError::NotInitialized, L"The library has not been initialized" }, + {WinToastError::SystemNotSupported, L"The OS does not support WinToast" }, + {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app" }, + {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one" }, + {WinToastError::InvalidParameters, L"Invalid parameters, please double-check the AUMI or App Name" }, + {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, + {WinToastError::UnknownError, L"Unknown error" } }; - const auto iter = Labels.find(error); + auto const iter = Labels.find(error); assert(iter != Labels.end()); return iter->second; } @@ -477,8 +482,7 @@ enum WinToast::ShortcutResult WinToast::createShortcut() { if (FAILED(initHr) && initHr != S_FALSE) { DEBUG_MSG(L"Error on COM library initialization!"); return SHORTCUT_COM_INIT_FAILURE; - } - else { + } else { _hasCoInitialized = true; } } @@ -486,8 +490,9 @@ enum WinToast::ShortcutResult WinToast::createShortcut() { bool wasChanged; HRESULT hr = validateShellLinkHelper(wasChanged); - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; + } hr = createShellLinkHelper(); return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; @@ -503,7 +508,6 @@ bool WinToast::initialize(_Out_opt_ WinToastError* error) { 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?"); @@ -532,17 +536,16 @@ bool WinToast::isInitialized() const { return _isInitialized; } -const std::wstring& WinToast::appName() const { +std::wstring const& WinToast::appName() const { return _appName; } -const std::wstring& WinToast::appUserModelId() const { +std::wstring const& WinToast::appUserModelId() const { return _aumi; } - -HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { - WCHAR path[MAX_PATH] = { L'\0' }; +HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { + WCHAR path[MAX_PATH] = {L'\0'}; Util::defaultShellLinkPath(_appName, path); // Check if the file exist DWORD attr = GetFileAttributesW(path); @@ -572,7 +575,7 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); if (SUCCEEDED(hr)) { WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); + hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); wasChanged = false; if (FAILED(hr) || _aumi != AUMI) { if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { @@ -603,15 +606,13 @@ HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { return hr; } - - -HRESULT WinToast::createShellLinkHelper() { +HRESULT WinToast::createShellLinkHelper() { if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { - return E_FAIL; + return E_FAIL; } - WCHAR exePath[MAX_PATH]{L'\0'}; - WCHAR slPath[MAX_PATH]{L'\0'}; + WCHAR exePath[MAX_PATH]{L'\0'}; + WCHAR slPath[MAX_PATH]{L'\0'}; Util::defaultShellLinkPath(_appName, slPath); Util::defaultExecutablePath(exePath); ComPtr shellLink; @@ -650,7 +651,7 @@ HRESULT WinToast::createShellLinkHelper() { return hr; } -INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { +INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { std::shared_ptr handler(eventHandler); setError(error, WinToastError::NoError); INT64 id = -1; @@ -666,55 +667,21 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand } ComPtr notificationManager; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + HRESULT hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); if (SUCCEEDED(hr)) { ComPtr notifier; hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); if (SUCCEEDED(hr)) { ComPtr notificationFactory; - hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); + hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); if (SUCCEEDED(hr)) { ComPtr xmlDocument; - ToastTemplateType toast_type = ToastTemplateType::ToastTemplateType_ToastImageAndText02; - if (toast.isToastGeneric()) - { - switch (toast.type()) - { - case WinToastTemplate::WinToastTemplateType::HeroImageAndImageAndText01: - toast_type = ToastTemplateType_ToastImageAndText01; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndImageAndText02: - toast_type = ToastTemplateType_ToastImageAndText02; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndImageAndText03: - toast_type = ToastTemplateType_ToastImageAndText03; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndImageAndText04: - toast_type = ToastTemplateType_ToastImageAndText04; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndText01: - toast_type = ToastTemplateType_ToastText01; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndText02: - toast_type = ToastTemplateType_ToastText02; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndText03: - toast_type = ToastTemplateType_ToastText03; - break; - case WinToastTemplate::WinToastTemplateType::HeroImageAndText04: - toast_type = ToastTemplateType_ToastText04; - break; - default: - toast_type = ToastTemplateType(toast.type()); - } - } - else - { - toast_type = ToastTemplateType(toast.type()); - } - hr = notificationManager->GetTemplateContent(toast_type, &xmlDocument); - if (SUCCEEDED(hr) && toast.isToastGeneric()) + hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); + if (SUCCEEDED(hr) && toast.isToastGeneric()) { hr = setBindToastGenericHelper(xmlDocument.Get()); + } if (SUCCEEDED(hr)) { for (UINT32 i = 0, fieldsCount = static_cast(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); @@ -722,11 +689,9 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand // Modern feature are supported Windows > Windows 10 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. - if (SUCCEEDED(hr) - && !toast.attributionText().empty()) { + if (SUCCEEDED(hr) && !toast.attributionText().empty()) { hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); } @@ -738,12 +703,13 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand if (SUCCEEDED(hr)) { hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) - ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); + ? 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"); + (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); } if (SUCCEEDED(hr)) { @@ -756,10 +722,13 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand if (SUCCEEDED(hr)) { bool isWin10AnniversaryOrAbove = WinToast::isWin10AnniversaryOrHigher(); - bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; - hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) : hr; - if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) + bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; + hr = toast.hasImage() + ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) + : hr; + if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) { hr = setHeroImageHelper(xmlDocument.Get(), toast.heroImagePath(), toast.isInlineHeroImage()); + } if (SUCCEEDED(hr)) { ComPtr notification; hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); @@ -768,18 +737,17 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand if (relativeExpiration > 0) { InternalDateTime expirationDateTime(relativeExpiration); expiration = expirationDateTime; - hr = notification->put_ExpirationTime(&expirationDateTime); + hr = notification->put_ExpirationTime(&expirationDateTime); } EventRegistrationToken activatedToken, dismissedToken, failedToken; GUID guid; HRESULT hrGuid = CoCreateGuid(&guid); - id = guid.Data1; + id = guid.Data1; if (SUCCEEDED(hr) && SUCCEEDED(hrGuid)) { - std::function processForDeletionFunc = [this, id]()-> void { processForDeletion(id); }; - hr = Util::setEventHandlers(notification.Get(), handler, expiration, - activatedToken, dismissedToken, failedToken, processForDeletionFunc); + hr = Util::setEventHandlers(notification.Get(), handler, expiration, activatedToken, dismissedToken, + failedToken, [this, id]() { markAsReadyForDeletion(id); }); if (FAILED(hr)) { setError(error, WinToastError::InvalidHandler); } @@ -805,10 +773,11 @@ INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHand return FAILED(hr) ? -1 : id; } -ComPtr WinToast::notifier(_In_ bool* succeded) const { +ComPtr WinToast::notifier(_In_ bool* succeded) const { ComPtr notificationManager; ComPtr notifier; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + HRESULT hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); if (SUCCEEDED(hr)) { hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); } @@ -816,36 +785,22 @@ ComPtr WinToast::notifier(_In_ bool* succeded) const { return notifier; } -void WinToast::processForDeletion(_In_ INT64 id) -{ - // delete previous NotifyData which has been setForDeletion = true - deletePreviousNotify(); - - // setForDeletion in the next processForDeletion() because it is still in use, so cannot delete. - setForDeletion(id); -} - -void WinToast::setForDeletion(_In_ INT64 id) -{ - _buffer[id]._setForDeletion = true; -} - -void WinToast::deletePreviousNotify() -{ - bool found = false; - do - { - auto it = std::find_if(_buffer.begin(), _buffer.end(), [](const std::pair& data) - -> bool { return data.second._setForDeletion; }); - if (it != _buffer.end()) - { - found = true; +void WinToast::markAsReadyForDeletion(_In_ INT64 id) { + // Flush the buffer by removing all the toasts that are ready for deletion + for (auto it = _buffer.begin(); it != _buffer.end();) { + if (it->second.isReadyForDeletion()) { it->second.RemoveTokens(); - _buffer.erase(it); + it = _buffer.erase(it); + } else { + ++it; } - else - found = false; - } while (found); + } + + // Mark the toast as ready for deletion (if it exists) so that it will be removed from the buffer in the next iteration + auto const iter = _buffer.find(id); + if (iter != _buffer.end()) { + _buffer[id].markAsReadyForDeletion(); + } } bool WinToast::hideToast(_In_ INT64 id) { @@ -854,30 +809,37 @@ bool WinToast::hideToast(_In_ INT64 id) { return false; } - if (_buffer.find(id) != _buffer.end()) { - auto succeded = false; - auto notify = notifier(&succeded); - if (succeded) { - auto result = notify->Hide(_buffer[id]._notify.Get()); - _buffer[id].RemoveTokens(); - _buffer.erase(id); - return SUCCEEDED(result); - } + auto iter = _buffer.find(id); + if (iter == _buffer.end()) { + return false; + } + + auto succeded = false; + auto notify = notifier(&succeded); + if (!succeded) { + return false; } - return false; + + auto& notifyData = iter->second; + auto result = notify->Hide(notifyData.notification()); + notifyData.RemoveTokens(); + _buffer.erase(iter); + return SUCCEEDED(result); } void WinToast::clear() { auto succeded = false; - auto notify = notifier(&succeded); - if (succeded) { - auto end = _buffer.end(); - for (auto it = _buffer.begin(); it != end; ++it) { - notify->Hide(it->second._notify.Get()); - it->second.RemoveTokens(); - } - _buffer.clear(); + auto notify = notifier(&succeded); + if (!succeded) { + return; + } + + for (auto& data : _buffer) { + auto& notifyData = data.second; + notify->Hide(notifyData.notification()); + notifyData.RemoveTokens(); } + _buffer.clear(); } // @@ -887,8 +849,8 @@ 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_ const std::wstring& text) { - Util::createElement(xml, L"binding", L"text", { L"placement" }); +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); if (SUCCEEDED(hr)) { @@ -921,7 +883,7 @@ HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ con return hr; } -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) { +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)) { @@ -934,8 +896,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr ComPtr toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), - WinToastStringWrapper(duration).Get()); + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); } } } @@ -943,7 +904,7 @@ HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstr return hr; } -HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) { +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)) { @@ -956,8 +917,7 @@ HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstr ComPtr toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), - WinToastStringWrapper(scenario).Get()); + hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), WinToastStringWrapper(scenario).Get()); } } } @@ -965,7 +925,7 @@ HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstr return hr; } -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) { +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)) { @@ -991,8 +951,7 @@ HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { ComPtr toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), - WinToastStringWrapper(L"ToastGeneric").Get()); + hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); } } } @@ -1000,11 +959,12 @@ HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { return hr; } -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_ bool isToastGeneric, _In_ bool isCropHintCircle) { +HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, + _In_ bool isCropHintCircle) { assert(path.size() < MAX_PATH); wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); + HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); if (SUCCEEDED(hr)) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); @@ -1014,13 +974,13 @@ HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::ws ComPtr imageElement; HRESULT hrImage = node.As(&imageElement); - if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) - { + if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"appLogoOverride").Get()); - if (SUCCEEDED(hr) && isCropHintCircle) + if (SUCCEEDED(hr) && isCropHintCircle) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"hint-crop").Get(), WinToastStringWrapper(L"circle").Get()); + } } - if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) { ComPtr attributes; hr = node->get_Attributes(&attributes); if (SUCCEEDED(hr)) { @@ -1036,11 +996,18 @@ HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::ws return hr; } -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) { +HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, + _In_opt_ WinToastTemplate::AudioOption option) { std::vector attrs; - if (!path.empty()) attrs.push_back(L"src"); - if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop"); - if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent"); + if (!path.empty()) { + attrs.push_back(L"src"); + } + if (option == WinToastTemplate::AudioOption::Loop) { + attrs.push_back(L"loop"); + } + if (option == WinToastTemplate::AudioOption::Silent) { + attrs.push_back(L"silent"); + } Util::createElement(xml, L"toast", L"audio", attrs); ComPtr nodeList; @@ -1064,19 +1031,19 @@ HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::ws 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; + 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; } } } @@ -1085,7 +1052,7 @@ HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::ws return hr; } -HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) { +HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& content, _In_ std::wstring const& arguments) { ComPtr nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); if (SUCCEEDED(hr)) { @@ -1105,10 +1072,14 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin if (SUCCEEDED(hr)) { ComPtr toastElement; hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), + WinToastStringWrapper(L"ToastGeneric").Get()); + } + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), + WinToastStringWrapper(L"long").Get()); + } if (SUCCEEDED(hr)) { ComPtr actionsElement; hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); @@ -1127,10 +1098,12 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin if (SUCCEEDED(hr)) { ComPtr actionElement; hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); - if (SUCCEEDED(hr)) + } + if (SUCCEEDED(hr)) { hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); + } if (SUCCEEDED(hr)) { ComPtr actionNode; hr = actionElement.As(&actionNode); @@ -1145,7 +1118,7 @@ HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstrin return hr; } -HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& path, _In_ bool isInlineImage) { +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)) { @@ -1159,10 +1132,12 @@ HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ const std::wst if (SUCCEEDED(hr)) { ComPtr imageElement; hr = xml->CreateElement(WinToastStringWrapper(L"image").Get(), &imageElement); - if (SUCCEEDED(hr) && isInlineImage == false) + if (SUCCEEDED(hr) && isInlineImage == false) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"hero").Get()); - if (SUCCEEDED(hr)) + } + if (SUCCEEDED(hr)) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"src").Get(), WinToastStringWrapper(path).Get()); + } if (SUCCEEDED(hr)) { ComPtr actionNode; hr = imageElement.As(&actionNode); @@ -1184,64 +1159,64 @@ void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value } WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { - static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3 , 1, 2, 2, 3 }; - _textFields = std::vector(TextFieldsCount[type], L""); + constexpr static std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3}; + _textFields = std::vector(TextFieldsCount[type], L""); } WinToastTemplate::~WinToastTemplate() { _textFields.clear(); } -void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) { - const auto position = static_cast(pos); +void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { + auto const position = static_cast(pos); assert(position < _textFields.size()); _textFields[position] = txt; } -void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath, _In_ CropHint cropHint) { +void WinToastTemplate::setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint) { _imagePath = imgPath; - _cropHint = cropHint; + _cropHint = cropHint; } -void WinToastTemplate::setHeroImagePath(_In_ const std::wstring& imgPath, _In_ bool inlineImage) { - _heroImagePath = imgPath; +void WinToastTemplate::setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage) { + _heroImagePath = imgPath; _inlineHeroImage = inlineImage; } -void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) { +void WinToastTemplate::setAudioPath(_In_ std::wstring const& audioPath) { _audioPath = audioPath; } void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { static const std::unordered_map Files = { - {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"}, - {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"}, - {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"}, - {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"}, - {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"}, - {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"}, - {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"}, - {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"}, - {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"}, - {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"}, - {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"}, - {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"}, - {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"}, - {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"}, - {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, - {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"}, - {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"}, - {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"}, - {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"}, - {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"}, - {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"}, - {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"}, - {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"}, - {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"}, - {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"}, - {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"}, + {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default" }, + {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM" }, + {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail" }, + {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder" }, + {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS" }, + {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm" }, + {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2" }, + {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3" }, + {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4" }, + {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5" }, + {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6" }, + {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7" }, + {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8" }, + {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9" }, + {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, + {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call" }, + {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1" }, + {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2" }, + {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3" }, + {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4" }, + {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5" }, + {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6" }, + {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7" }, + {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8" }, + {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9" }, + {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10" }, }; - const auto iter = Files.find(file); + auto const iter = Files.find(file); assert(iter != Files.end()); _audioPath = iter->second; } @@ -1250,15 +1225,15 @@ void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOp _audioOption = audioOption; } -void WinToastTemplate::setFirstLine(_In_ const std::wstring &text) { +void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::FirstLine); } -void WinToastTemplate::setSecondLine(_In_ const std::wstring &text) { +void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::SecondLine); } -void WinToastTemplate::setThirdLine(_In_ const std::wstring &text) { +void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::ThirdLine); } @@ -1272,18 +1247,26 @@ void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { switch (scenario) { - case Scenario::Default: _scenario = L"Default"; break; - case Scenario::Alarm: _scenario = L"Alarm"; break; - case Scenario::IncomingCall: _scenario = L"IncomingCall"; break; - case Scenario::Reminder: _scenario = L"Reminder"; break; + case Scenario::Default: + _scenario = L"Default"; + break; + case Scenario::Alarm: + _scenario = L"Alarm"; + break; + case Scenario::IncomingCall: + _scenario = L"IncomingCall"; + break; + case Scenario::Reminder: + _scenario = L"Reminder"; + break; } } -void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) { +void WinToastTemplate::setAttributionText(_In_ std::wstring const& attributionText) { _attributionText = attributionText; } -void WinToastTemplate::addAction(_In_ const std::wstring & label) { +void WinToastTemplate::addAction(_In_ std::wstring const& label) { _actions.push_back(label); } @@ -1296,56 +1279,45 @@ std::size_t WinToastTemplate::actionsCount() const { } bool WinToastTemplate::hasImage() const { - return (_type < WinToastTemplateType::Text01 - || _type == WinToastTemplate::HeroImageAndImageAndText01 - || _type == WinToastTemplate::HeroImageAndImageAndText02 - || _type == WinToastTemplate::HeroImageAndImageAndText03 - || _type == WinToastTemplate::HeroImageAndImageAndText04); + return _type < WinToastTemplateType::Text01; } bool WinToastTemplate::hasHeroImage() const { - return (_type == WinToastTemplate::HeroImageAndText01 - || _type == WinToastTemplate::HeroImageAndText02 - || _type == WinToastTemplate::HeroImageAndText03 - || _type == WinToastTemplate::HeroImageAndText04 - || _type == WinToastTemplate::HeroImageAndImageAndText01 - || _type == WinToastTemplate::HeroImageAndImageAndText02 - || _type == WinToastTemplate::HeroImageAndImageAndText03 - || _type == WinToastTemplate::HeroImageAndImageAndText04); + return hasImage() && !_heroImagePath.empty(); } -const std::vector& WinToastTemplate::textFields() const { +std::vector const& WinToastTemplate::textFields() const { return _textFields; } -const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const { - const auto position = static_cast(pos); +std::wstring const& WinToastTemplate::textField(_In_ TextField pos) const { + auto const position = static_cast(pos); assert(position < _textFields.size()); return _textFields[position]; } -const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const { +std::wstring const& WinToastTemplate::actionLabel(_In_ std::size_t position) const { assert(position < _actions.size()); return _actions[position]; } -const std::wstring& WinToastTemplate::imagePath() const { +std::wstring const& WinToastTemplate::imagePath() const { return _imagePath; } -const std::wstring& WinToastTemplate::heroImagePath() const { +std::wstring const& WinToastTemplate::heroImagePath() const { return _heroImagePath; } -const std::wstring& WinToastTemplate::audioPath() const { +std::wstring const& WinToastTemplate::audioPath() const { return _audioPath; } -const std::wstring& WinToastTemplate::attributionText() const { +std::wstring const& WinToastTemplate::attributionText() const { return _attributionText; } -const std::wstring& WinToastLib::WinToastTemplate::scenario() const { +std::wstring const& WinToastLib::WinToastTemplate::scenario() const { return _scenario; } @@ -1366,15 +1338,7 @@ WinToastTemplate::Duration WinToastTemplate::duration() const { } bool WinToastTemplate::isToastGeneric() const { - return ( _type == WinToastTemplate::HeroImageAndText01 - || _type == WinToastTemplate::HeroImageAndText02 - || _type == WinToastTemplate::HeroImageAndText03 - || _type == WinToastTemplate::HeroImageAndText04 - || _type == WinToastTemplate::HeroImageAndImageAndText01 - || _type == WinToastTemplate::HeroImageAndImageAndText02 - || _type == WinToastTemplate::HeroImageAndImageAndText03 - || _type == WinToastTemplate::HeroImageAndImageAndText04 - || _cropHint == WinToastTemplate::Circle); + return hasHeroImage() || _cropHint == WinToastTemplate::Circle; } bool WinToastTemplate::isInlineHeroImage() const { diff --git a/src/wintoastlib.h b/src/wintoastlib.h deleted file mode 100644 index 1181f0a..0000000 --- a/src/wintoastlib.h +++ /dev/null @@ -1,296 +0,0 @@ -/* * Copyright (C) 2016-2019 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 -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; - - -namespace WinToastLib { - - 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 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, - HeroImageAndImageAndText01, - HeroImageAndImageAndText02, - HeroImageAndImageAndText03, - HeroImageAndImageAndText04, - HeroImageAndText01, - HeroImageAndText02, - HeroImageAndText03, - HeroImageAndText04, - }; - - 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_ const std::wstring& text); - void setSecondLine(_In_ const std::wstring& text); - void setThirdLine(_In_ const std::wstring& text); - void setTextField(_In_ const std::wstring& txt, _In_ TextField pos); - void setAttributionText(_In_ const std::wstring & attributionText); - void setImagePath(_In_ const std::wstring& imgPath, _In_ CropHint cropHint = CropHint::Square); - void setHeroImagePath(_In_ const std::wstring& imgPath, bool inlineImage); - void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); - void setAudioPath(_In_ const std::wstring& 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_ const std::wstring& label); - - std::size_t textFieldsCount() const; - std::size_t actionsCount() const; - bool hasImage() const; - bool hasHeroImage() const; - const std::vector& textFields() const; - const std::wstring& textField(_In_ TextField pos) const; - const std::wstring& actionLabel(_In_ std::size_t pos) const; - const std::wstring& imagePath() const; - const std::wstring& heroImagePath() const; - const std::wstring& audioPath() const; - const std::wstring& attributionText() const; - const std::wstring& scenario() const; - INT64 expiration() const; - WinToastTemplateType type() const; - WinToastTemplate::AudioOption audioOption() const; - Duration duration() const; - bool isToastGeneric() const; - bool isInlineHeroImage() const; - bool isCropHintCircle() const; - private: - 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, - 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 - }; - - enum ShortcutPolicy { - /* Don't check, create, or modify a shortcut. */ - SHORTCUT_POLICY_IGNORE = 0, - /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ - SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, - /* Require a shortcut with matching AUMI, create if missing, modify if not matching. - * This is the default. */ - SHORTCUT_POLICY_REQUIRE_CREATE = 2, - }; - - WinToast(void); - virtual ~WinToast(); - static WinToast* instance(); - static bool isCompatible(); - static bool isSupportingModernFeatures(); - static bool isWin10AnniversaryOrHigher(); - 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()); - static const std::wstring& 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_ const WinToastTemplate& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error = nullptr); - virtual void clear(); - virtual enum ShortcutResult createShortcut(); - - const std::wstring& appName() const; - const std::wstring& appUserModelId() const; - void setAppUserModelId(_In_ const std::wstring& aumi); - void setAppName(_In_ const std::wstring& appName); - void setShortcutPolicy(_In_ ShortcutPolicy policy); - - protected: - struct NotifyData - { - NotifyData() : _setForDeletion(false), _destroyed(false) {} - NotifyData(_In_ ComPtr notify, - _In_ EventRegistrationToken activatedToken, - _In_ EventRegistrationToken dismissedToken, - _In_ EventRegistrationToken failedToken) - : _notify(notify) - , _activatedToken(activatedToken) - , _dismissedToken(dismissedToken) - , _failedToken(failedToken) - , _setForDeletion(false) - , _destroyed(false) - {} - // Never call RemoveTokens() in NotifyData's destructor! - // Because it will remove tokens before the notification happens. - void RemoveTokens() - { - if (_destroyed == false && _notify.Get()) - { - _notify->remove_Activated(_activatedToken); - _notify->remove_Dismissed(_dismissedToken); - _notify->remove_Failed(_failedToken); - _destroyed = true; - } - } - ComPtr _notify; - bool _setForDeletion; - private: - EventRegistrationToken _activatedToken; - EventRegistrationToken _dismissedToken; - EventRegistrationToken _failedToken; - bool _destroyed; - }; - bool _isInitialized{false}; - bool _hasCoInitialized{false}; - ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; - std::wstring _appName{}; - std::wstring _aumi{}; - std::map _buffer{}; - - void processForDeletion(_In_ INT64 id); - void setForDeletion(_In_ INT64 id); - void deletePreviousNotify(); - HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_ bool isToastGeneric, bool isCropHintCircle); - HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& path, _In_ bool isInlineImage); - HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); - 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_ UINT32 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 addScenarioHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& scenario); - ComPtr notifier(_In_ bool* succeded) const; - void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); - }; -} -#endif // WINTOASTLIB_H