|
| 1 | +/** |
| 2 | + * MIT License |
| 3 | + * |
| 4 | + * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui <mohabouje@gmail.com> |
| 5 | + * |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy of |
| 7 | + * this software and associated documentation files (the "Software"), to deal in |
| 8 | + * the Software without restriction, including without limitation the rights to |
| 9 | + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| 10 | + * the Software, and to permit persons to whom the Software is furnished to do so, |
| 11 | + * subject to the following conditions: |
| 12 | + * |
| 13 | + * The above copyright notice and this permission notice shall be included in all |
| 14 | + * copies or substantial portions of the Software. |
| 15 | + * |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| 18 | + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| 19 | + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| 20 | + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 21 | + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 22 | + */ |
| 23 | + |
| 24 | +#ifndef WINTOASTLIB_H |
| 25 | +#define WINTOASTLIB_H |
| 26 | + |
| 27 | +#include <Windows.h> |
| 28 | +#include <sdkddkver.h> |
| 29 | +#include <WinUser.h> |
| 30 | +#include <ShObjIdl.h> |
| 31 | +#include <wrl/implements.h> |
| 32 | +#include <wrl/event.h> |
| 33 | +#include <windows.ui.notifications.h> |
| 34 | +#include <strsafe.h> |
| 35 | +#include <Psapi.h> |
| 36 | +#include <ShlObj.h> |
| 37 | +#include <roapi.h> |
| 38 | +#include <propvarutil.h> |
| 39 | +#include <functiondiscoverykeys.h> |
| 40 | +#include <iostream> |
| 41 | +#include <winstring.h> |
| 42 | +#include <string.h> |
| 43 | +#include <vector> |
| 44 | +#include <map> |
| 45 | +#include <memory> |
| 46 | + |
| 47 | +using namespace Microsoft::WRL; |
| 48 | +using namespace ABI::Windows::Data::Xml::Dom; |
| 49 | +using namespace ABI::Windows::Foundation; |
| 50 | +using namespace ABI::Windows::UI::Notifications; |
| 51 | +using namespace Windows::Foundation; |
| 52 | + |
| 53 | +namespace WinToastLib { |
| 54 | + |
| 55 | + class IWinToastHandler { |
| 56 | + public: |
| 57 | + enum WinToastDismissalReason { |
| 58 | + UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, |
| 59 | + ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, |
| 60 | + TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut |
| 61 | + }; |
| 62 | + |
| 63 | + virtual ~IWinToastHandler() = default; |
| 64 | + virtual void toastActivated() const = 0; |
| 65 | + virtual void toastActivated(int actionIndex) const = 0; |
| 66 | + virtual void toastActivated(const char* response) const = 0; |
| 67 | + virtual void toastDismissed(WinToastDismissalReason state) const = 0; |
| 68 | + virtual void toastFailed() const = 0; |
| 69 | + }; |
| 70 | + |
| 71 | + class WinToastTemplate { |
| 72 | + public: |
| 73 | + enum class Scenario { Default, Alarm, IncomingCall, Reminder }; |
| 74 | + enum Duration { System, Short, Long }; |
| 75 | + enum AudioOption { Default = 0, Silent, Loop }; |
| 76 | + enum TextField { FirstLine = 0, SecondLine, ThirdLine }; |
| 77 | + |
| 78 | + enum WinToastTemplateType { |
| 79 | + ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, |
| 80 | + ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, |
| 81 | + ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, |
| 82 | + ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, |
| 83 | + Text01 = ToastTemplateType::ToastTemplateType_ToastText01, |
| 84 | + Text02 = ToastTemplateType::ToastTemplateType_ToastText02, |
| 85 | + Text03 = ToastTemplateType::ToastTemplateType_ToastText03, |
| 86 | + Text04 = ToastTemplateType::ToastTemplateType_ToastText04 |
| 87 | + }; |
| 88 | + |
| 89 | + enum AudioSystemFile { |
| 90 | + DefaultSound, |
| 91 | + IM, |
| 92 | + Mail, |
| 93 | + Reminder, |
| 94 | + SMS, |
| 95 | + Alarm, |
| 96 | + Alarm2, |
| 97 | + Alarm3, |
| 98 | + Alarm4, |
| 99 | + Alarm5, |
| 100 | + Alarm6, |
| 101 | + Alarm7, |
| 102 | + Alarm8, |
| 103 | + Alarm9, |
| 104 | + Alarm10, |
| 105 | + Call, |
| 106 | + Call1, |
| 107 | + Call2, |
| 108 | + Call3, |
| 109 | + Call4, |
| 110 | + Call5, |
| 111 | + Call6, |
| 112 | + Call7, |
| 113 | + Call8, |
| 114 | + Call9, |
| 115 | + Call10, |
| 116 | + }; |
| 117 | + |
| 118 | + enum CropHint { |
| 119 | + Square, |
| 120 | + Circle, |
| 121 | + }; |
| 122 | + |
| 123 | + WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); |
| 124 | + ~WinToastTemplate(); |
| 125 | + |
| 126 | + void setFirstLine(_In_ std::wstring const& text); |
| 127 | + void setSecondLine(_In_ std::wstring const& text); |
| 128 | + void setThirdLine(_In_ std::wstring const& text); |
| 129 | + void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); |
| 130 | + void setAttributionText(_In_ std::wstring const& attributionText); |
| 131 | + void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); |
| 132 | + void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); |
| 133 | + void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); |
| 134 | + void setAudioPath(_In_ std::wstring const& audioPath); |
| 135 | + void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); |
| 136 | + void setDuration(_In_ Duration duration); |
| 137 | + void setExpiration(_In_ INT64 millisecondsFromNow); |
| 138 | + void setScenario(_In_ Scenario scenario); |
| 139 | + void addAction(_In_ std::wstring const& label); |
| 140 | + void addInput(); |
| 141 | + |
| 142 | + std::size_t textFieldsCount() const; |
| 143 | + std::size_t actionsCount() const; |
| 144 | + bool hasImage() const; |
| 145 | + bool hasHeroImage() const; |
| 146 | + std::vector<std::wstring> const& textFields() const; |
| 147 | + std::wstring const& textField(_In_ TextField pos) const; |
| 148 | + std::wstring const& actionLabel(_In_ std::size_t pos) const; |
| 149 | + std::wstring const& imagePath() const; |
| 150 | + std::wstring const& heroImagePath() const; |
| 151 | + std::wstring const& audioPath() const; |
| 152 | + std::wstring const& attributionText() const; |
| 153 | + std::wstring const& scenario() const; |
| 154 | + INT64 expiration() const; |
| 155 | + WinToastTemplateType type() const; |
| 156 | + WinToastTemplate::AudioOption audioOption() const; |
| 157 | + Duration duration() const; |
| 158 | + bool isToastGeneric() const; |
| 159 | + bool isInlineHeroImage() const; |
| 160 | + bool isCropHintCircle() const; |
| 161 | + bool isInput() const; |
| 162 | + |
| 163 | + private: |
| 164 | + bool _hasInput{false}; |
| 165 | + |
| 166 | + std::vector<std::wstring> _textFields{}; |
| 167 | + std::vector<std::wstring> _actions{}; |
| 168 | + std::wstring _imagePath{}; |
| 169 | + std::wstring _heroImagePath{}; |
| 170 | + bool _inlineHeroImage{false}; |
| 171 | + std::wstring _audioPath{}; |
| 172 | + std::wstring _attributionText{}; |
| 173 | + std::wstring _scenario{L"Default"}; |
| 174 | + INT64 _expiration{0}; |
| 175 | + AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; |
| 176 | + WinToastTemplateType _type{WinToastTemplateType::Text01}; |
| 177 | + Duration _duration{Duration::System}; |
| 178 | + CropHint _cropHint{CropHint::Square}; |
| 179 | + }; |
| 180 | + |
| 181 | + class WinToast { |
| 182 | + public: |
| 183 | + enum WinToastError { |
| 184 | + NoError = 0, |
| 185 | + NotInitialized, |
| 186 | + SystemNotSupported, |
| 187 | + ShellLinkNotCreated, |
| 188 | + InvalidAppUserModelID, |
| 189 | + InvalidParameters, |
| 190 | + InvalidHandler, |
| 191 | + NotDisplayed, |
| 192 | + UnknownError |
| 193 | + }; |
| 194 | + |
| 195 | + enum ShortcutResult { |
| 196 | + SHORTCUT_UNCHANGED = 0, |
| 197 | + SHORTCUT_WAS_CHANGED = 1, |
| 198 | + SHORTCUT_WAS_CREATED = 2, |
| 199 | + |
| 200 | + SHORTCUT_MISSING_PARAMETERS = -1, |
| 201 | + SHORTCUT_INCOMPATIBLE_OS = -2, |
| 202 | + SHORTCUT_COM_INIT_FAILURE = -3, |
| 203 | + SHORTCUT_CREATE_FAILED = -4 |
| 204 | + }; |
| 205 | + |
| 206 | + enum ShortcutPolicy { |
| 207 | + /* Don't check, create, or modify a shortcut. */ |
| 208 | + SHORTCUT_POLICY_IGNORE = 0, |
| 209 | + /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ |
| 210 | + SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, |
| 211 | + /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ |
| 212 | + SHORTCUT_POLICY_REQUIRE_CREATE = 2, |
| 213 | + }; |
| 214 | + |
| 215 | + WinToast(void); |
| 216 | + virtual ~WinToast(); |
| 217 | + static WinToast* instance(); |
| 218 | + static bool isCompatible(); |
| 219 | + static bool isSupportingModernFeatures(); |
| 220 | + static bool isWin10AnniversaryOrHigher(); |
| 221 | + static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, |
| 222 | + _In_ std::wstring const& subProduct = std::wstring(), |
| 223 | + _In_ std::wstring const& versionInformation = std::wstring()); |
| 224 | + static std::wstring const& strerror(_In_ WinToastError error); |
| 225 | + virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); |
| 226 | + virtual bool isInitialized() const; |
| 227 | + virtual bool hideToast(_In_ INT64 id); |
| 228 | + virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, |
| 229 | + _Out_opt_ WinToastError* error = nullptr); |
| 230 | + virtual void clear(); |
| 231 | + virtual enum ShortcutResult createShortcut(); |
| 232 | + |
| 233 | + std::wstring const& appName() const; |
| 234 | + std::wstring const& appUserModelId() const; |
| 235 | + void setAppUserModelId(_In_ std::wstring const& aumi); |
| 236 | + void setAppName(_In_ std::wstring const& appName); |
| 237 | + void setShortcutPolicy(_In_ ShortcutPolicy policy); |
| 238 | + |
| 239 | + protected: |
| 240 | + struct NotifyData { |
| 241 | + NotifyData(){}; |
| 242 | + NotifyData(_In_ ComPtr<IToastNotification> notify, _In_ EventRegistrationToken activatedToken, |
| 243 | + _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : |
| 244 | + _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} |
| 245 | + |
| 246 | + ~NotifyData() { |
| 247 | + RemoveTokens(); |
| 248 | + } |
| 249 | + |
| 250 | + void RemoveTokens() { |
| 251 | + if (!_readyForDeletion) { |
| 252 | + return; |
| 253 | + } |
| 254 | + |
| 255 | + if (_previouslyTokenRemoved) { |
| 256 | + return; |
| 257 | + } |
| 258 | + |
| 259 | + if (!_notify.Get()) { |
| 260 | + return; |
| 261 | + } |
| 262 | + |
| 263 | + _notify->remove_Activated(_activatedToken); |
| 264 | + _notify->remove_Dismissed(_dismissedToken); |
| 265 | + _notify->remove_Failed(_failedToken); |
| 266 | + _previouslyTokenRemoved = true; |
| 267 | + } |
| 268 | + |
| 269 | + void markAsReadyForDeletion() { |
| 270 | + _readyForDeletion = true; |
| 271 | + } |
| 272 | + |
| 273 | + bool isReadyForDeletion() const { |
| 274 | + return _readyForDeletion; |
| 275 | + } |
| 276 | + |
| 277 | + IToastNotification* notification() { |
| 278 | + return _notify.Get(); |
| 279 | + } |
| 280 | + |
| 281 | + private: |
| 282 | + ComPtr<IToastNotification> _notify{nullptr}; |
| 283 | + EventRegistrationToken _activatedToken{}; |
| 284 | + EventRegistrationToken _dismissedToken{}; |
| 285 | + EventRegistrationToken _failedToken{}; |
| 286 | + bool _readyForDeletion{false}; |
| 287 | + bool _previouslyTokenRemoved{false}; |
| 288 | + }; |
| 289 | + |
| 290 | + bool _isInitialized{false}; |
| 291 | + bool _hasCoInitialized{false}; |
| 292 | + ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; |
| 293 | + std::wstring _appName{}; |
| 294 | + std::wstring _aumi{}; |
| 295 | + std::map<INT64, NotifyData> _buffer{}; |
| 296 | + |
| 297 | + void markAsReadyForDeletion(_In_ INT64 id); |
| 298 | + HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); |
| 299 | + HRESULT createShellLinkHelper(); |
| 300 | + HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); |
| 301 | + HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); |
| 302 | + HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); |
| 303 | + HRESULT |
| 304 | + setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, |
| 305 | + _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); |
| 306 | + HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); |
| 307 | + HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); |
| 308 | + HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); |
| 309 | + HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); |
| 310 | + HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); |
| 311 | + HRESULT addInputHelper(_In_ IXmlDocument* xml); |
| 312 | + ComPtr<IToastNotifier> notifier(_In_ bool* succeded) const; |
| 313 | + void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); |
| 314 | + }; |
| 315 | +} // namespace WinToastLib |
| 316 | +#endif // WINTOASTLIB_H |
0 commit comments