Backed out 3 changesets (bug 1883476, bug 1826375) for causing windows build bustages...
[gecko.git] / third_party / WinToast / wintoastlib.cpp
blobea5648a61d629932e6e20a8976f31113fbd3013d
1 /* * Copyright (C) 2016-2019 Mohammed Boujemaoui <mohabouje@gmail.com>
3 * Permission is hereby granted, free of charge, to any person obtaining a copy of
4 * this software and associated documentation files (the "Software"), to deal in
5 * the Software without restriction, including without limitation the rights to
6 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 * the Software, and to permit persons to whom the Software is furnished to do so,
8 * subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in all
11 * copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 #include "wintoastlib.h"
22 #include <memory>
23 #include <assert.h>
24 #include <unordered_map>
25 #include <array>
27 #pragma comment(lib,"shlwapi")
28 #pragma comment(lib,"user32")
30 #ifdef NDEBUG
31 #define DEBUG_MSG(str) do { } while ( false )
32 #else
33 #define DEBUG_MSG(str) do { std::wcout << str << std::endl; } while( false )
34 #endif
36 #define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\"
37 #define DEFAULT_LINK_FORMAT L".lnk"
38 #define STATUS_SUCCESS (0x00000000)
41 // Quickstart: Handling toast activations from Win32 apps in Windows 10
42 // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/
43 using namespace WinToastLib;
44 namespace DllImporter {
46 // Function load a function from library
47 template <typename Function>
48 HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) {
49 if (!library) {
50 return E_INVALIDARG;
52 func = reinterpret_cast<Function>(GetProcAddress(library, name));
53 return (func != nullptr) ? S_OK : E_FAIL;
56 typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID);
57 typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch);
58 typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory);
59 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);
60 typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_ UINT32 *length);
61 typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string);
63 static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID;
64 static f_PropVariantToString PropVariantToString;
65 static f_RoGetActivationFactory RoGetActivationFactory;
66 static f_WindowsCreateStringReference WindowsCreateStringReference;
67 static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer;
68 static f_WindowsDeleteString WindowsDeleteString;
71 template<class T>
72 _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) {
73 return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory));
76 template<typename T>
77 inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef<T> factory) noexcept {
78 return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf());
81 inline HRESULT initialize() {
82 HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL");
83 HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID);
84 if (SUCCEEDED(hr)) {
85 HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL");
86 hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString);
87 if (SUCCEEDED(hr)) {
88 HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL");
89 const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory))
90 && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference))
91 && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer))
92 && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString));
93 return succeded ? S_OK : E_FAIL;
96 return hr;
100 class WinToastStringWrapper {
101 public:
102 WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept {
103 HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
104 if (!SUCCEEDED(hr)) {
105 RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
109 WinToastStringWrapper(_In_ const std::wstring &stringRef) noexcept {
110 HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast<UINT32>(stringRef.length()), &_header, &_hstring);
111 if (FAILED(hr)) {
112 RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
116 ~WinToastStringWrapper() {
117 DllImporter::WindowsDeleteString(_hstring);
120 inline HSTRING Get() const noexcept {
121 return _hstring;
123 private:
124 HSTRING _hstring;
125 HSTRING_HEADER _header;
129 class InternalDateTime : public IReference<DateTime> {
130 public:
131 static INT64 Now() {
132 FILETIME now;
133 GetSystemTimeAsFileTime(&now);
134 return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime);
137 InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {}
139 InternalDateTime(INT64 millisecondsFromNow) {
140 _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000;
143 virtual ~InternalDateTime() = default;
145 operator INT64() {
146 return _dateTime.UniversalTime;
149 HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) {
150 *dateTime = _dateTime;
151 return S_OK;
154 HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) {
155 if (!ppvObject) {
156 return E_POINTER;
158 if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference<DateTime>)) {
159 *ppvObject = static_cast<IUnknown*>(static_cast<IReference<DateTime>*>(this));
160 return S_OK;
162 return E_NOINTERFACE;
165 ULONG STDMETHODCALLTYPE Release() {
166 return 1;
169 ULONG STDMETHODCALLTYPE AddRef() {
170 return 2;
173 HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) {
174 return E_NOTIMPL;
177 HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) {
178 return E_NOTIMPL;
181 HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) {
182 return E_NOTIMPL;
185 protected:
186 DateTime _dateTime;
189 namespace Util {
191 typedef LONG NTSTATUS, *PNTSTATUS;
192 typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
193 inline RTL_OSVERSIONINFOW getRealOSVersion() {
194 HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
195 if (hMod) {
196 RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
197 if (fxPtr != nullptr) {
198 RTL_OSVERSIONINFOW rovi = { 0 };
199 rovi.dwOSVersionInfoSize = sizeof(rovi);
200 if (STATUS_SUCCESS == fxPtr(&rovi)) {
201 return rovi;
205 RTL_OSVERSIONINFOW rovi = { 0 };
206 return rovi;
209 inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
210 DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize);
211 DEBUG_MSG("Default executable path: " << path);
212 return (written > 0) ? S_OK : E_FAIL;
216 inline HRESULT commonShellLinksDirectory(_In_ const WCHAR* baseEnv, _In_ WCHAR* path, _In_ DWORD nSize) {
217 DWORD written = GetEnvironmentVariableW(baseEnv, path, nSize);
218 HRESULT hr = written > 0 ? S_OK : E_INVALIDARG;
219 if (SUCCEEDED(hr)) {
220 errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH);
221 hr = (result == 0) ? S_OK : E_INVALIDARG;
222 DEBUG_MSG("Default shell link path: " << path);
224 return hr;
227 inline HRESULT commonShellLinkPath(_In_ const WCHAR* baseEnv, const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize) {
228 HRESULT hr = commonShellLinksDirectory(baseEnv, path, nSize);
229 if (SUCCEEDED(hr)) {
230 const std::wstring appLink(appname + DEFAULT_LINK_FORMAT);
231 errno_t result = wcscat_s(path, nSize, appLink.c_str());
232 hr = (result == 0) ? S_OK : E_INVALIDARG;
233 DEBUG_MSG("Default shell link file path: " << path);
235 return hr;
238 inline HRESULT defaultUserShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
239 return commonShellLinkPath(L"APPDATA", appname, path, nSize);
242 inline HRESULT defaultSystemShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) {
243 return commonShellLinkPath(L"PROGRAMDATA", appname, path, nSize);
246 inline PCWSTR AsString(ComPtr<IXmlDocument> &xmlDocument) {
247 HSTRING xml;
248 ComPtr<IXmlNodeSerializer> ser;
249 HRESULT hr = xmlDocument.As<IXmlNodeSerializer>(&ser);
250 hr = ser->GetXml(&xml);
251 if (SUCCEEDED(hr))
252 return DllImporter::WindowsGetStringRawBuffer(xml, nullptr);
253 return nullptr;
256 inline PCWSTR AsString(HSTRING hstring) {
257 return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr);
260 inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) {
261 ComPtr<IXmlText> textNode;
262 HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode);
263 if (SUCCEEDED(hr)) {
264 ComPtr<IXmlNode> stringNode;
265 hr = textNode.As(&stringNode);
266 if (SUCCEEDED(hr)) {
267 ComPtr<IXmlNode> appendedChild;
268 hr = node->AppendChild(stringNode.Get(), &appendedChild);
271 return hr;
274 inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr<IWinToastHandler> eventHandler, _In_ INT64 expirationTime) {
275 EventRegistrationToken activatedToken, dismissedToken, failedToken;
276 HRESULT hr = notification->add_Activated(
277 Callback < Implements < RuntimeClassFlags<ClassicCom>,
278 ITypedEventHandler<ToastNotification*, IInspectable* >> >(
279 [eventHandler](IToastNotification*, IInspectable* inspectable)
281 IToastActivatedEventArgs *activatedEventArgs;
282 HRESULT hr = inspectable->QueryInterface(&activatedEventArgs);
283 if (SUCCEEDED(hr)) {
284 HSTRING argumentsHandle;
285 hr = activatedEventArgs->get_Arguments(&argumentsHandle);
286 if (SUCCEEDED(hr)) {
287 PCWSTR arguments = Util::AsString(argumentsHandle);
288 if (arguments && *arguments) {
289 eventHandler->toastActivated(static_cast<int>(wcstol(arguments, nullptr, 10)));
290 return S_OK;
294 eventHandler->toastActivated();
295 return S_OK;
296 }).Get(), &activatedToken);
298 if (SUCCEEDED(hr)) {
299 hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags<ClassicCom>,
300 ITypedEventHandler<ToastNotification*, ToastDismissedEventArgs* >> >(
301 [eventHandler, expirationTime](IToastNotification*, IToastDismissedEventArgs* e)
303 ToastDismissalReason reason;
304 if (SUCCEEDED(e->get_Reason(&reason)))
306 if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime)
307 reason = ToastDismissalReason_TimedOut;
308 eventHandler->toastDismissed(static_cast<IWinToastHandler::WinToastDismissalReason>(reason));
310 return S_OK;
311 }).Get(), &dismissedToken);
312 if (SUCCEEDED(hr)) {
313 hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags<ClassicCom>,
314 ITypedEventHandler<ToastNotification*, ToastFailedEventArgs* >> >(
315 [eventHandler](IToastNotification*, IToastFailedEventArgs*)
317 eventHandler->toastFailed();
318 return S_OK;
319 }).Get(), &failedToken);
322 return hr;
325 inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) {
326 ComPtr<ABI::Windows::Data::Xml::Dom::IXmlAttribute> srcAttribute;
327 HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute);
328 if (SUCCEEDED(hr)) {
329 ComPtr<IXmlNode> node;
330 hr = srcAttribute.As(&node);
331 if (SUCCEEDED(hr)) {
332 ComPtr<IXmlNode> pNode;
333 hr = attributeMap->SetNamedItem(node.Get(), &pNode);
336 return hr;
339 inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring& root_node, _In_ const std::wstring& element_name, _In_ const std::vector<std::wstring>& attribute_names) {
340 ComPtr<IXmlNodeList> rootList;
341 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList);
342 if (SUCCEEDED(hr)) {
343 ComPtr<IXmlNode> root;
344 hr = rootList->Item(0, &root);
345 if (SUCCEEDED(hr)) {
346 ComPtr<ABI::Windows::Data::Xml::Dom::IXmlElement> audioElement;
347 hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement);
348 if (SUCCEEDED(hr)) {
349 ComPtr<IXmlNode> audioNodeTmp;
350 hr = audioElement.As(&audioNodeTmp);
351 if (SUCCEEDED(hr)) {
352 ComPtr<IXmlNode> audioNode;
353 hr = root->AppendChild(audioNodeTmp.Get(), &audioNode);
354 if (SUCCEEDED(hr)) {
355 ComPtr<IXmlNamedNodeMap> attributes;
356 hr = audioNode->get_Attributes(&attributes);
357 if (SUCCEEDED(hr)) {
358 for (const auto& it : attribute_names) {
359 hr = addAttribute(xml, it, attributes.Get());
367 return hr;
371 WinToast* WinToast::instance() {
372 static WinToast instance;
373 return &instance;
376 WinToast::WinToast() :
377 _isInitialized(false),
378 _hasCoInitialized(false)
380 if (!isCompatible()) {
381 DEBUG_MSG(L"Warning: Your system is not compatible with this library ");
385 WinToast::~WinToast() {
386 if (_hasCoInitialized) {
387 CoUninitialize();
391 void WinToast::setAppName(_In_ const std::wstring& appName) {
392 _appName = appName;
396 void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) {
397 _aumi = aumi;
398 DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str());
401 void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) {
402 _shortcutPolicy = shortcutPolicy;
405 bool WinToast::isCompatible() {
406 DllImporter::initialize();
407 return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr)
408 || (DllImporter::PropVariantToString == nullptr)
409 || (DllImporter::RoGetActivationFactory == nullptr)
410 || (DllImporter::WindowsCreateStringReference == nullptr)
411 || (DllImporter::WindowsDeleteString == nullptr));
414 bool WinToastLib::WinToast::isSupportingModernFeatures() {
415 constexpr auto MinimumSupportedVersion = 6;
416 return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion;
419 std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName,
420 _In_ const std::wstring &productName,
421 _In_ const std::wstring &subProduct,
422 _In_ const std::wstring &versionInformation)
424 std::wstring aumi = companyName;
425 aumi += L"." + productName;
426 if (subProduct.length() > 0) {
427 aumi += L"." + subProduct;
428 if (versionInformation.length() > 0) {
429 aumi += L"." + versionInformation;
433 if (aumi.length() > SCHAR_MAX) {
434 DEBUG_MSG("Error: max size allowed for AUMI: 128 characters.");
436 return aumi;
439 const std::wstring& WinToast::strerror(WinToastError error) {
440 static const std::unordered_map<WinToastError, std::wstring> Labels = {
441 {WinToastError::NoError, L"No error. The process was executed correctly"},
442 {WinToastError::NotInitialized, L"The library has not been initialized"},
443 {WinToastError::SystemNotSupported, L"The OS does not support WinToast"},
444 {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app"},
445 {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one"},
446 {WinToastError::InvalidParameters, L"The parameters used to configure the library are not valid normally because an invalid AUMI or App Name"},
447 {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"},
448 {WinToastError::UnknownError, L"Unknown error"}
451 const auto iter = Labels.find(error);
452 assert(iter != Labels.end());
453 return iter->second;
456 enum WinToast::ShortcutResult WinToast::createShortcut() {
457 if (_aumi.empty() || _appName.empty()) {
458 DEBUG_MSG(L"Error: App User Model Id or Appname is empty!");
459 return SHORTCUT_MISSING_PARAMETERS;
462 if (!isCompatible()) {
463 DEBUG_MSG(L"Your OS is not compatible with this library! =(");
464 return SHORTCUT_INCOMPATIBLE_OS;
467 if (!_hasCoInitialized) {
468 HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED);
469 if (initHr != RPC_E_CHANGED_MODE) {
470 if (FAILED(initHr) && initHr != S_FALSE) {
471 DEBUG_MSG(L"Error on COM library initialization!");
472 return SHORTCUT_COM_INIT_FAILURE;
474 else {
475 _hasCoInitialized = true;
480 bool wasChanged;
481 HRESULT hr = validateShellLinkHelper(wasChanged);
482 if (SUCCEEDED(hr))
483 return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED;
485 hr = createShellLinkHelper();
486 return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED;
489 bool WinToast::initialize(_Out_ WinToastError* error) {
490 _isInitialized = false;
491 setError(error, WinToastError::NoError);
493 if (!isCompatible()) {
494 setError(error, WinToastError::SystemNotSupported);
495 DEBUG_MSG(L"Error: system not supported.");
496 return false;
500 if (_aumi.empty() || _appName.empty()) {
501 setError(error, WinToastError::InvalidParameters);
502 DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?");
503 return false;
506 if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) {
507 if (createShortcut() < 0) {
508 setError(error, WinToastError::ShellLinkNotCreated);
509 DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
510 return false;
514 if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) {
515 setError(error, WinToastError::InvalidAppUserModelID);
516 DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =(");
517 return false;
520 _isInitialized = true;
521 return _isInitialized;
524 bool WinToast::isInitialized() const {
525 return _isInitialized;
528 const std::wstring& WinToast::appName() const {
529 return _appName;
532 const std::wstring& WinToast::appUserModelId() const {
533 return _aumi;
537 HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) {
538 WCHAR path[MAX_PATH] = { L'\0' };
539 Util::defaultUserShellLinkPath(_appName, path);
540 // Check if the file exist
541 DWORD attr = GetFileAttributesW(path);
542 if (attr >= 0xFFFFFFF) {
543 // The shortcut may be in the system Start Menu.
544 WCHAR systemPath[MAX_PATH] = { L'\0' };
545 Util::defaultSystemShellLinkPath(_appName, systemPath);
546 attr = GetFileAttributesW(systemPath);
547 if (attr >= 0xFFFFFFF) {
548 DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path);
549 return E_FAIL;
551 wcscpy(path, systemPath);
554 // Let's load the file as shell link to validate.
555 // - Create a shell link
556 // - Create a persistant file
557 // - Load the path as data for the persistant file
558 // - Read the property AUMI and validate with the current
559 // - Review if AUMI is equal.
560 ComPtr<IShellLink> shellLink;
561 HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
562 if (SUCCEEDED(hr)) {
563 ComPtr<IPersistFile> persistFile;
564 hr = shellLink.As(&persistFile);
565 if (SUCCEEDED(hr)) {
566 hr = persistFile->Load(path, STGM_READ);
567 if (SUCCEEDED(hr)) {
568 ComPtr<IPropertyStore> propertyStore;
569 hr = shellLink.As(&propertyStore);
570 if (SUCCEEDED(hr)) {
571 PROPVARIANT appIdPropVar;
572 hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar);
573 if (SUCCEEDED(hr)) {
574 WCHAR AUMI[MAX_PATH];
575 hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH);
576 wasChanged = false;
577 if (FAILED(hr) || _aumi != AUMI) {
578 if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) {
579 // AUMI Changed for the same app, let's update the current value! =)
580 wasChanged = true;
581 PropVariantClear(&appIdPropVar);
582 hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
583 if (SUCCEEDED(hr)) {
584 hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
585 if (SUCCEEDED(hr)) {
586 hr = propertyStore->Commit();
587 if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) {
588 hr = persistFile->Save(path, TRUE);
592 } else {
593 // Not allowed to touch the shortcut to fix the AUMI
594 hr = E_FAIL;
597 PropVariantClear(&appIdPropVar);
603 return hr;
608 HRESULT WinToast::createShellLinkHelper() {
609 if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) {
610 return E_FAIL;
613 WCHAR exePath[MAX_PATH]{L'\0'};
614 WCHAR slPath[MAX_PATH]{L'\0'};
615 Util::defaultUserShellLinkPath(_appName, slPath);
616 Util::defaultExecutablePath(exePath);
617 ComPtr<IShellLinkW> shellLink;
618 HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
619 if (SUCCEEDED(hr)) {
620 hr = shellLink->SetPath(exePath);
621 if (SUCCEEDED(hr)) {
622 hr = shellLink->SetArguments(L"");
623 if (SUCCEEDED(hr)) {
624 hr = shellLink->SetWorkingDirectory(exePath);
625 if (SUCCEEDED(hr)) {
626 ComPtr<IPropertyStore> propertyStore;
627 hr = shellLink.As(&propertyStore);
628 if (SUCCEEDED(hr)) {
629 PROPVARIANT appIdPropVar;
630 hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar);
631 if (SUCCEEDED(hr)) {
632 hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
633 if (SUCCEEDED(hr)) {
634 hr = propertyStore->Commit();
635 if (SUCCEEDED(hr)) {
636 ComPtr<IPersistFile> persistFile;
637 hr = shellLink.As(&persistFile);
638 if (SUCCEEDED(hr)) {
639 hr = persistFile->Save(slPath, TRUE);
643 PropVariantClear(&appIdPropVar);
650 return hr;
653 INT64 WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ IWinToastHandler* handler, _Out_ WinToastError* error) {
654 setError(error, WinToastError::NoError);
655 INT64 id = -1;
656 if (!isInitialized()) {
657 setError(error, WinToastError::NotInitialized);
658 DEBUG_MSG("Error when launching the toast. WinToast is not initialized.");
659 return id;
661 if (!handler) {
662 setError(error, WinToastError::InvalidHandler);
663 DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr.");
664 return id;
667 ComPtr<IToastNotificationManagerStatics> notificationManager;
668 HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager);
669 if (SUCCEEDED(hr)) {
670 ComPtr<IToastNotifier> notifier;
671 hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier);
672 if (SUCCEEDED(hr)) {
673 ComPtr<IToastNotificationFactory> notificationFactory;
674 hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &notificationFactory);
675 if (SUCCEEDED(hr)) {
676 ComPtr<IXmlDocument> xmlDocument;
677 HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument);
678 if (SUCCEEDED(hr)) {
679 for (std::size_t i = 0, fieldsCount = toast.textFieldsCount(); i < fieldsCount && SUCCEEDED(hr); i++) {
680 hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i);
683 // Modern feature are supported Windows > Windows 10
684 if (SUCCEEDED(hr) && isSupportingModernFeatures()) {
686 // Note that we do this *after* using toast.textFieldsCount() to
687 // iterate/fill the template's text fields, since we're adding yet another text field.
688 if (SUCCEEDED(hr)
689 && !toast.attributionText().empty()) {
690 hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText());
693 std::array<WCHAR, 12> buf;
694 for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) {
695 _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i);
696 hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data());
699 if (SUCCEEDED(hr)) {
700 hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default)
701 ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption());
704 if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) {
705 hr = addDurationHelper(xmlDocument.Get(),
706 (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long");
709 if (SUCCEEDED(hr)) {
710 hr = addScenarioHelper(xmlDocument.Get(), toast.scenario());
713 } else {
714 DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version");
717 if (SUCCEEDED(hr)) {
718 hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr;
719 if (SUCCEEDED(hr)) {
720 ComPtr<IToastNotification> notification;
721 hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), &notification);
722 if (SUCCEEDED(hr)) {
723 INT64 expiration = 0, relativeExpiration = toast.expiration();
724 if (relativeExpiration > 0) {
725 InternalDateTime expirationDateTime(relativeExpiration);
726 expiration = expirationDateTime;
727 hr = notification->put_ExpirationTime(&expirationDateTime);
730 if (SUCCEEDED(hr)) {
731 hr = Util::setEventHandlers(notification.Get(), std::shared_ptr<IWinToastHandler>(handler), expiration);
732 if (FAILED(hr)) {
733 setError(error, WinToastError::InvalidHandler);
737 if (SUCCEEDED(hr)) {
738 GUID guid;
739 hr = CoCreateGuid(&guid);
740 if (SUCCEEDED(hr)) {
741 id = guid.Data1;
742 _buffer[id] = notification;
743 DEBUG_MSG("xml: " << Util::AsString(xmlDocument));
744 hr = notifier->Show(notification.Get());
745 if (FAILED(hr)) {
746 setError(error, WinToastError::NotDisplayed);
757 return FAILED(hr) ? -1 : id;
760 ComPtr<IToastNotifier> WinToast::notifier(_In_ bool* succeded) const {
761 ComPtr<IToastNotificationManagerStatics> notificationManager;
762 ComPtr<IToastNotifier> notifier;
763 HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager);
764 if (SUCCEEDED(hr)) {
765 hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier);
767 *succeded = SUCCEEDED(hr);
768 return notifier;
771 bool WinToast::hideToast(_In_ INT64 id) {
772 if (!isInitialized()) {
773 DEBUG_MSG("Error when hiding the toast. WinToast is not initialized.");
774 return false;
777 if (_buffer.find(id) != _buffer.end()) {
778 auto succeded = false;
779 auto notify = notifier(&succeded);
780 if (succeded) {
781 auto result = notify->Hide(_buffer[id].Get());
782 _buffer.erase(id);
783 return SUCCEEDED(result);
786 return false;
789 void WinToast::clear() {
790 auto succeded = false;
791 auto notify = notifier(&succeded);
792 if (succeded) {
793 auto end = _buffer.end();
794 for (auto it = _buffer.begin(); it != end; ++it) {
795 notify->Hide(it->second.Get());
797 _buffer.clear();
802 // Available as of Windows 10 Anniversary Update
803 // Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts
805 // NOTE: This will add a new text field, so be aware when iterating over
806 // the toast's text fields or getting a count of them.
808 HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) {
809 Util::createElement(xml, L"binding", L"text", { L"placement" });
810 ComPtr<IXmlNodeList> nodeList;
811 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
812 if (SUCCEEDED(hr)) {
813 UINT32 nodeListLength;
814 hr = nodeList->get_Length(&nodeListLength);
815 if (SUCCEEDED(hr)) {
816 for (UINT32 i = 0; i < nodeListLength; i++) {
817 ComPtr<IXmlNode> textNode;
818 hr = nodeList->Item(i, &textNode);
819 if (SUCCEEDED(hr)) {
820 ComPtr<IXmlNamedNodeMap> attributes;
821 hr = textNode->get_Attributes(&attributes);
822 if (SUCCEEDED(hr)) {
823 ComPtr<IXmlNode> editedNode;
824 if (SUCCEEDED(hr)) {
825 hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode);
826 if (FAILED(hr) || !editedNode) {
827 continue;
829 hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml);
830 if (SUCCEEDED(hr)) {
831 return setTextFieldHelper(xml, text, i);
839 return hr;
842 HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) {
843 ComPtr<IXmlNodeList> nodeList;
844 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
845 if (SUCCEEDED(hr)) {
846 UINT32 length;
847 hr = nodeList->get_Length(&length);
848 if (SUCCEEDED(hr)) {
849 ComPtr<IXmlNode> toastNode;
850 hr = nodeList->Item(0, &toastNode);
851 if (SUCCEEDED(hr)) {
852 ComPtr<IXmlElement> toastElement;
853 hr = toastNode.As(&toastElement);
854 if (SUCCEEDED(hr)) {
855 hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(),
856 WinToastStringWrapper(duration).Get());
861 return hr;
864 HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ const std::wstring& scenario) {
865 ComPtr<IXmlNodeList> nodeList;
866 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
867 if (SUCCEEDED(hr)) {
868 UINT32 length;
869 hr = nodeList->get_Length(&length);
870 if (SUCCEEDED(hr)) {
871 ComPtr<IXmlNode> toastNode;
872 hr = nodeList->Item(0, &toastNode);
873 if (SUCCEEDED(hr)) {
874 ComPtr<IXmlElement> toastElement;
875 hr = toastNode.As(&toastElement);
876 if (SUCCEEDED(hr)) {
877 hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(),
878 WinToastStringWrapper(scenario).Get());
883 return hr;
886 HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) {
887 ComPtr<IXmlNodeList> nodeList;
888 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
889 if (SUCCEEDED(hr)) {
890 ComPtr<IXmlNode> node;
891 hr = nodeList->Item(pos, &node);
892 if (SUCCEEDED(hr)) {
893 hr = Util::setNodeStringValue(text, node.Get(), xml);
896 return hr;
900 HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) {
901 assert(path.size() < MAX_PATH);
903 wchar_t imagePath[MAX_PATH] = L"file:///";
904 HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str());
905 if (SUCCEEDED(hr)) {
906 ComPtr<IXmlNodeList> nodeList;
907 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList);
908 if (SUCCEEDED(hr)) {
909 ComPtr<IXmlNode> node;
910 hr = nodeList->Item(0, &node);
911 if (SUCCEEDED(hr)) {
912 ComPtr<IXmlNamedNodeMap> attributes;
913 hr = node->get_Attributes(&attributes);
914 if (SUCCEEDED(hr)) {
915 ComPtr<IXmlNode> editedNode;
916 hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
917 if (SUCCEEDED(hr)) {
918 Util::setNodeStringValue(imagePath, editedNode.Get(), xml);
924 return hr;
927 HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) {
928 std::vector<std::wstring> attrs;
929 if (!path.empty()) attrs.push_back(L"src");
930 if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop");
931 if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent");
932 Util::createElement(xml, L"toast", L"audio", attrs);
934 ComPtr<IXmlNodeList> nodeList;
935 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList);
936 if (SUCCEEDED(hr)) {
937 ComPtr<IXmlNode> node;
938 hr = nodeList->Item(0, &node);
939 if (SUCCEEDED(hr)) {
940 ComPtr<IXmlNamedNodeMap> attributes;
941 hr = node->get_Attributes(&attributes);
942 if (SUCCEEDED(hr)) {
943 ComPtr<IXmlNode> editedNode;
944 if (!path.empty()) {
945 if (SUCCEEDED(hr)) {
946 hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
947 if (SUCCEEDED(hr)) {
948 hr = Util::setNodeStringValue(path, editedNode.Get(), xml);
953 if (SUCCEEDED(hr)) {
954 switch (option) {
955 case WinToastTemplate::AudioOption::Loop:
956 hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode);
957 if (SUCCEEDED(hr)) {
958 hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
960 break;
961 case WinToastTemplate::AudioOption::Silent:
962 hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode);
963 if (SUCCEEDED(hr)) {
964 hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
966 default:
967 break;
973 return hr;
976 HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) {
977 ComPtr<IXmlNodeList> nodeList;
978 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList);
979 if (SUCCEEDED(hr)) {
980 UINT32 length;
981 hr = nodeList->get_Length(&length);
982 if (SUCCEEDED(hr)) {
983 ComPtr<IXmlNode> actionsNode;
984 if (length > 0) {
985 hr = nodeList->Item(0, &actionsNode);
986 } else {
987 hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
988 if (SUCCEEDED(hr)) {
989 hr = nodeList->get_Length(&length);
990 if (SUCCEEDED(hr)) {
991 ComPtr<IXmlNode> toastNode;
992 hr = nodeList->Item(0, &toastNode);
993 if (SUCCEEDED(hr)) {
994 ComPtr<IXmlElement> toastElement;
995 hr = toastNode.As(&toastElement);
996 if (SUCCEEDED(hr))
997 hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get());
998 if (SUCCEEDED(hr))
999 hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get());
1000 if (SUCCEEDED(hr)) {
1001 ComPtr<IXmlElement> actionsElement;
1002 hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement);
1003 if (SUCCEEDED(hr)) {
1004 hr = actionsElement.As(&actionsNode);
1005 if (SUCCEEDED(hr)) {
1006 ComPtr<IXmlNode> appendedChild;
1007 hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild);
1015 if (SUCCEEDED(hr)) {
1016 ComPtr<IXmlElement> actionElement;
1017 hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement);
1018 if (SUCCEEDED(hr))
1019 hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get());
1020 if (SUCCEEDED(hr))
1021 hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get());
1022 if (SUCCEEDED(hr)) {
1023 ComPtr<IXmlNode> actionNode;
1024 hr = actionElement.As(&actionNode);
1025 if (SUCCEEDED(hr)) {
1026 ComPtr<IXmlNode> appendedChild;
1027 hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
1033 return hr;
1036 void WinToast::setError(_Out_ WinToastError* error, _In_ WinToastError value) {
1037 if (error) {
1038 *error = value;
1042 WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) {
1043 static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3};
1044 _textFields = std::vector<std::wstring>(TextFieldsCount[type], L"");
1047 WinToastTemplate::~WinToastTemplate() {
1048 _textFields.clear();
1051 void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) {
1052 const auto position = static_cast<std::size_t>(pos);
1053 assert(position < _textFields.size());
1054 _textFields[position] = txt;
1057 void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) {
1058 _imagePath = imgPath;
1061 void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) {
1062 _audioPath = audioPath;
1065 void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) {
1066 static const std::unordered_map<AudioSystemFile, std::wstring> Files = {
1067 {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"},
1068 {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"},
1069 {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"},
1070 {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"},
1071 {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"},
1072 {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"},
1073 {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"},
1074 {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"},
1075 {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"},
1076 {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"},
1077 {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"},
1078 {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"},
1079 {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"},
1080 {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"},
1081 {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"},
1082 {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"},
1083 {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"},
1084 {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"},
1085 {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"},
1086 {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"},
1087 {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"},
1088 {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"},
1089 {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"},
1090 {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"},
1091 {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"},
1092 {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"},
1094 const auto iter = Files.find(file);
1095 assert(iter != Files.end());
1096 _audioPath = iter->second;
1099 void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) {
1100 _audioOption = audioOption;
1103 void WinToastTemplate::setFirstLine(const std::wstring &text) {
1104 setTextField(text, WinToastTemplate::FirstLine);
1107 void WinToastTemplate::setSecondLine(const std::wstring &text) {
1108 setTextField(text, WinToastTemplate::SecondLine);
1111 void WinToastTemplate::setThirdLine(const std::wstring &text) {
1112 setTextField(text, WinToastTemplate::ThirdLine);
1115 void WinToastTemplate::setDuration(_In_ Duration duration) {
1116 _duration = duration;
1119 void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) {
1120 _expiration = millisecondsFromNow;
1123 void WinToastLib::WinToastTemplate::setScenario(Scenario scenario) {
1124 switch (scenario) {
1125 case Scenario::Default: _scenario = L"Default"; break;
1126 case Scenario::Alarm: _scenario = L"Alarm"; break;
1127 case Scenario::IncomingCall: _scenario = L"IncomingCall"; break;
1128 case Scenario::Reminder: _scenario = L"Reminder"; break;
1132 void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) {
1133 _attributionText = attributionText;
1136 void WinToastTemplate::addAction(_In_ const std::wstring & label) {
1137 _actions.push_back(label);
1140 std::size_t WinToastTemplate::textFieldsCount() const {
1141 return _textFields.size();
1144 std::size_t WinToastTemplate::actionsCount() const {
1145 return _actions.size();
1148 bool WinToastTemplate::hasImage() const {
1149 return _type < WinToastTemplateType::Text01;
1152 const std::vector<std::wstring>& WinToastTemplate::textFields() const {
1153 return _textFields;
1156 const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const {
1157 const auto position = static_cast<std::size_t>(pos);
1158 assert(position < _textFields.size());
1159 return _textFields[position];
1162 const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const {
1163 assert(position < _actions.size());
1164 return _actions[position];
1167 const std::wstring& WinToastTemplate::imagePath() const {
1168 return _imagePath;
1171 const std::wstring& WinToastTemplate::audioPath() const {
1172 return _audioPath;
1175 const std::wstring& WinToastTemplate::attributionText() const {
1176 return _attributionText;
1179 const std::wstring& WinToastLib::WinToastTemplate::scenario() const {
1180 return _scenario;
1183 INT64 WinToastTemplate::expiration() const {
1184 return _expiration;
1187 WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const {
1188 return _type;
1191 WinToastTemplate::AudioOption WinToastTemplate::audioOption() const {
1192 return _audioOption;
1195 WinToastTemplate::Duration WinToastTemplate::duration() const {
1196 return _duration;