Bug 1690340 - Part 5: Remove the menu separators from the developer tools menu. r...
[gecko.git] / third_party / WinToast / wintoastlib.cpp
blob7e8c307e91a42d5f83ce7cc7355007a4a9ac4ec0
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 } else {
710 DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version");
713 if (SUCCEEDED(hr)) {
714 hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr;
715 if (SUCCEEDED(hr)) {
716 ComPtr<IToastNotification> notification;
717 hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), &notification);
718 if (SUCCEEDED(hr)) {
719 INT64 expiration = 0, relativeExpiration = toast.expiration();
720 if (relativeExpiration > 0) {
721 InternalDateTime expirationDateTime(relativeExpiration);
722 expiration = expirationDateTime;
723 hr = notification->put_ExpirationTime(&expirationDateTime);
726 if (SUCCEEDED(hr)) {
727 hr = Util::setEventHandlers(notification.Get(), std::shared_ptr<IWinToastHandler>(handler), expiration);
728 if (FAILED(hr)) {
729 setError(error, WinToastError::InvalidHandler);
733 if (SUCCEEDED(hr)) {
734 GUID guid;
735 hr = CoCreateGuid(&guid);
736 if (SUCCEEDED(hr)) {
737 id = guid.Data1;
738 _buffer[id] = notification;
739 DEBUG_MSG("xml: " << Util::AsString(xmlDocument));
740 hr = notifier->Show(notification.Get());
741 if (FAILED(hr)) {
742 setError(error, WinToastError::NotDisplayed);
753 return FAILED(hr) ? -1 : id;
756 ComPtr<IToastNotifier> WinToast::notifier(_In_ bool* succeded) const {
757 ComPtr<IToastNotificationManagerStatics> notificationManager;
758 ComPtr<IToastNotifier> notifier;
759 HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager);
760 if (SUCCEEDED(hr)) {
761 hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier);
763 *succeded = SUCCEEDED(hr);
764 return notifier;
767 bool WinToast::hideToast(_In_ INT64 id) {
768 if (!isInitialized()) {
769 DEBUG_MSG("Error when hiding the toast. WinToast is not initialized.");
770 return false;
773 if (_buffer.find(id) != _buffer.end()) {
774 auto succeded = false;
775 auto notify = notifier(&succeded);
776 if (succeded) {
777 auto result = notify->Hide(_buffer[id].Get());
778 _buffer.erase(id);
779 return SUCCEEDED(result);
782 return false;
785 void WinToast::clear() {
786 auto succeded = false;
787 auto notify = notifier(&succeded);
788 if (succeded) {
789 auto end = _buffer.end();
790 for (auto it = _buffer.begin(); it != end; ++it) {
791 notify->Hide(it->second.Get());
793 _buffer.clear();
798 // Available as of Windows 10 Anniversary Update
799 // Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts
801 // NOTE: This will add a new text field, so be aware when iterating over
802 // the toast's text fields or getting a count of them.
804 HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text) {
805 Util::createElement(xml, L"binding", L"text", { L"placement" });
806 ComPtr<IXmlNodeList> nodeList;
807 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
808 if (SUCCEEDED(hr)) {
809 UINT32 nodeListLength;
810 hr = nodeList->get_Length(&nodeListLength);
811 if (SUCCEEDED(hr)) {
812 for (UINT32 i = 0; i < nodeListLength; i++) {
813 ComPtr<IXmlNode> textNode;
814 hr = nodeList->Item(i, &textNode);
815 if (SUCCEEDED(hr)) {
816 ComPtr<IXmlNamedNodeMap> attributes;
817 hr = textNode->get_Attributes(&attributes);
818 if (SUCCEEDED(hr)) {
819 ComPtr<IXmlNode> editedNode;
820 if (SUCCEEDED(hr)) {
821 hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode);
822 if (FAILED(hr) || !editedNode) {
823 continue;
825 hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml);
826 if (SUCCEEDED(hr)) {
827 return setTextFieldHelper(xml, text, i);
835 return hr;
838 HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& duration) {
839 ComPtr<IXmlNodeList> nodeList;
840 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
841 if (SUCCEEDED(hr)) {
842 UINT32 length;
843 hr = nodeList->get_Length(&length);
844 if (SUCCEEDED(hr)) {
845 ComPtr<IXmlNode> toastNode;
846 hr = nodeList->Item(0, &toastNode);
847 if (SUCCEEDED(hr)) {
848 ComPtr<IXmlElement> toastElement;
849 hr = toastNode.As(&toastElement);
850 if (SUCCEEDED(hr)) {
851 hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(),
852 WinToastStringWrapper(duration).Get());
857 return hr;
860 HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& text, _In_ UINT32 pos) {
861 ComPtr<IXmlNodeList> nodeList;
862 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList);
863 if (SUCCEEDED(hr)) {
864 ComPtr<IXmlNode> node;
865 hr = nodeList->Item(pos, &node);
866 if (SUCCEEDED(hr)) {
867 hr = Util::setNodeStringValue(text, node.Get(), xml);
870 return hr;
874 HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path) {
875 assert(path.size() < MAX_PATH);
877 wchar_t imagePath[MAX_PATH] = L"file:///";
878 HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str());
879 if (SUCCEEDED(hr)) {
880 ComPtr<IXmlNodeList> nodeList;
881 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList);
882 if (SUCCEEDED(hr)) {
883 ComPtr<IXmlNode> node;
884 hr = nodeList->Item(0, &node);
885 if (SUCCEEDED(hr)) {
886 ComPtr<IXmlNamedNodeMap> attributes;
887 hr = node->get_Attributes(&attributes);
888 if (SUCCEEDED(hr)) {
889 ComPtr<IXmlNode> editedNode;
890 hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
891 if (SUCCEEDED(hr)) {
892 Util::setNodeStringValue(imagePath, editedNode.Get(), xml);
898 return hr;
901 HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& path, _In_opt_ WinToastTemplate::AudioOption option) {
902 std::vector<std::wstring> attrs;
903 if (!path.empty()) attrs.push_back(L"src");
904 if (option == WinToastTemplate::AudioOption::Loop) attrs.push_back(L"loop");
905 if (option == WinToastTemplate::AudioOption::Silent) attrs.push_back(L"silent");
906 Util::createElement(xml, L"toast", L"audio", attrs);
908 ComPtr<IXmlNodeList> nodeList;
909 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList);
910 if (SUCCEEDED(hr)) {
911 ComPtr<IXmlNode> node;
912 hr = nodeList->Item(0, &node);
913 if (SUCCEEDED(hr)) {
914 ComPtr<IXmlNamedNodeMap> attributes;
915 hr = node->get_Attributes(&attributes);
916 if (SUCCEEDED(hr)) {
917 ComPtr<IXmlNode> editedNode;
918 if (!path.empty()) {
919 if (SUCCEEDED(hr)) {
920 hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode);
921 if (SUCCEEDED(hr)) {
922 hr = Util::setNodeStringValue(path, editedNode.Get(), xml);
927 if (SUCCEEDED(hr)) {
928 switch (option) {
929 case WinToastTemplate::AudioOption::Loop:
930 hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode);
931 if (SUCCEEDED(hr)) {
932 hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
934 break;
935 case WinToastTemplate::AudioOption::Silent:
936 hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode);
937 if (SUCCEEDED(hr)) {
938 hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml);
940 default:
941 break;
947 return hr;
950 HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring& content, _In_ const std::wstring& arguments) {
951 ComPtr<IXmlNodeList> nodeList;
952 HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList);
953 if (SUCCEEDED(hr)) {
954 UINT32 length;
955 hr = nodeList->get_Length(&length);
956 if (SUCCEEDED(hr)) {
957 ComPtr<IXmlNode> actionsNode;
958 if (length > 0) {
959 hr = nodeList->Item(0, &actionsNode);
960 } else {
961 hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList);
962 if (SUCCEEDED(hr)) {
963 hr = nodeList->get_Length(&length);
964 if (SUCCEEDED(hr)) {
965 ComPtr<IXmlNode> toastNode;
966 hr = nodeList->Item(0, &toastNode);
967 if (SUCCEEDED(hr)) {
968 ComPtr<IXmlElement> toastElement;
969 hr = toastNode.As(&toastElement);
970 if (SUCCEEDED(hr))
971 hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get());
972 if (SUCCEEDED(hr))
973 hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get());
974 if (SUCCEEDED(hr)) {
975 ComPtr<IXmlElement> actionsElement;
976 hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement);
977 if (SUCCEEDED(hr)) {
978 hr = actionsElement.As(&actionsNode);
979 if (SUCCEEDED(hr)) {
980 ComPtr<IXmlNode> appendedChild;
981 hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild);
989 if (SUCCEEDED(hr)) {
990 ComPtr<IXmlElement> actionElement;
991 hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement);
992 if (SUCCEEDED(hr))
993 hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get());
994 if (SUCCEEDED(hr))
995 hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get());
996 if (SUCCEEDED(hr)) {
997 ComPtr<IXmlNode> actionNode;
998 hr = actionElement.As(&actionNode);
999 if (SUCCEEDED(hr)) {
1000 ComPtr<IXmlNode> appendedChild;
1001 hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
1007 return hr;
1010 void WinToast::setError(_Out_ WinToastError* error, _In_ WinToastError value) {
1011 if (error) {
1012 *error = value;
1016 WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) {
1017 static constexpr std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3};
1018 _textFields = std::vector<std::wstring>(TextFieldsCount[type], L"");
1021 WinToastTemplate::~WinToastTemplate() {
1022 _textFields.clear();
1025 void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ WinToastTemplate::TextField pos) {
1026 const auto position = static_cast<std::size_t>(pos);
1027 assert(position < _textFields.size());
1028 _textFields[position] = txt;
1031 void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) {
1032 _imagePath = imgPath;
1035 void WinToastTemplate::setAudioPath(_In_ const std::wstring& audioPath) {
1036 _audioPath = audioPath;
1039 void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) {
1040 static const std::unordered_map<AudioSystemFile, std::wstring> Files = {
1041 {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default"},
1042 {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM"},
1043 {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail"},
1044 {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder"},
1045 {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS"},
1046 {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm"},
1047 {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2"},
1048 {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3"},
1049 {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4"},
1050 {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5"},
1051 {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6"},
1052 {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7"},
1053 {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8"},
1054 {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9"},
1055 {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"},
1056 {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call"},
1057 {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1"},
1058 {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2"},
1059 {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3"},
1060 {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4"},
1061 {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5"},
1062 {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6"},
1063 {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7"},
1064 {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8"},
1065 {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9"},
1066 {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10"},
1068 const auto iter = Files.find(file);
1069 assert(iter != Files.end());
1070 _audioPath = iter->second;
1073 void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) {
1074 _audioOption = audioOption;
1077 void WinToastTemplate::setFirstLine(const std::wstring &text) {
1078 setTextField(text, WinToastTemplate::FirstLine);
1081 void WinToastTemplate::setSecondLine(const std::wstring &text) {
1082 setTextField(text, WinToastTemplate::SecondLine);
1085 void WinToastTemplate::setThirdLine(const std::wstring &text) {
1086 setTextField(text, WinToastTemplate::ThirdLine);
1089 void WinToastTemplate::setDuration(_In_ Duration duration) {
1090 _duration = duration;
1093 void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) {
1094 _expiration = millisecondsFromNow;
1097 void WinToastTemplate::setAttributionText(_In_ const std::wstring& attributionText) {
1098 _attributionText = attributionText;
1101 void WinToastTemplate::addAction(_In_ const std::wstring & label) {
1102 _actions.push_back(label);
1105 std::size_t WinToastTemplate::textFieldsCount() const {
1106 return _textFields.size();
1109 std::size_t WinToastTemplate::actionsCount() const {
1110 return _actions.size();
1113 bool WinToastTemplate::hasImage() const {
1114 return _type < WinToastTemplateType::Text01;
1117 const std::vector<std::wstring>& WinToastTemplate::textFields() const {
1118 return _textFields;
1121 const std::wstring& WinToastTemplate::textField(_In_ TextField pos) const {
1122 const auto position = static_cast<std::size_t>(pos);
1123 assert(position < _textFields.size());
1124 return _textFields[position];
1127 const std::wstring& WinToastTemplate::actionLabel(_In_ std::size_t position) const {
1128 assert(position < _actions.size());
1129 return _actions[position];
1132 const std::wstring& WinToastTemplate::imagePath() const {
1133 return _imagePath;
1136 const std::wstring& WinToastTemplate::audioPath() const {
1137 return _audioPath;
1140 const std::wstring& WinToastTemplate::attributionText() const {
1141 return _attributionText;
1144 INT64 WinToastTemplate::expiration() const {
1145 return _expiration;
1148 WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const {
1149 return _type;
1152 WinToastTemplate::AudioOption WinToastTemplate::audioOption() const {
1153 return _audioOption;
1156 WinToastTemplate::Duration WinToastTemplate::duration() const {
1157 return _duration;