diff --git a/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.cpp b/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.cpp deleted file mode 100644 index 6d1e0e59e1..0000000000 --- a/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.cpp +++ /dev/null @@ -1,1490 +0,0 @@ -/** - * MIT License - * - * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include "wintoastlib.h" - -#include -#include -#include -#include -#include - -#pragma comment(lib, "shlwapi") -#pragma comment(lib, "user32") - -#ifdef NDEBUG -# define DEBUG_MSG(str) \ - do { \ - } while (false) -#else -# define DEBUG_MSG(str) \ - do { \ - std::wcout << str << std::endl; \ - } while (false) -#endif - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -#define STATUS_SUCCESS (0x00000000) - -// Quickstart: Handling toast activations from Win32 apps in Windows 10 -// https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ -using namespace WinToastLib; -namespace DllImporter { - - // Function load a function from library - template - HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function& func) { - if (!library) { - return E_INVALIDARG; - } - func = reinterpret_cast(GetProcAddress(library, name)); - return (func != nullptr) ? S_OK : E_FAIL; - } - - typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, - _COM_Outptr_ void** factory); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, - _Out_ HSTRING_HEADER* hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); - typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); - typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); - - static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; - static f_PropVariantToString PropVariantToString; - static f_RoGetActivationFactory RoGetActivationFactory; - static f_WindowsCreateStringReference WindowsCreateStringReference; - static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; - static f_WindowsDeleteString WindowsDeleteString; - - template - __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { - return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); - } - - template - inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); - } - - inline HRESULT initialize() { - HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); - HRESULT hr = - loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); - if (SUCCEEDED(hr)) { - HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); - if (SUCCEEDED(hr)) { - HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); - bool const succeded = - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); - return succeded ? S_OK : E_FAIL; - } - } - return hr; - } -} // namespace DllImporter - -class WinToastStringWrapper { -public: - WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - WinToastStringWrapper(_In_ std::wstring const& stringRef) noexcept { - HRESULT hr = - DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); - if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - ~WinToastStringWrapper() { - DllImporter::WindowsDeleteString(_hstring); - } - - inline HSTRING Get() const noexcept { - return _hstring; - } - -private: - HSTRING _hstring; - HSTRING_HEADER _header; -}; - -class InternalDateTime : public IReference { -public: - static INT64 Now() { - FILETIME now; - GetSystemTimeAsFileTime(&now); - return ((((INT64) now.dwHighDateTime) << 32) | now.dwLowDateTime); - } - - InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} - - InternalDateTime(INT64 millisecondsFromNow) { - _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; - } - - virtual ~InternalDateTime() = default; - - operator INT64() { - return _dateTime.UniversalTime; - } - - HRESULT STDMETHODCALLTYPE get_Value(DateTime* dateTime) { - *dateTime = _dateTime; - return S_OK; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) { - if (!ppvObject) { - return E_POINTER; - } - if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference)) { - *ppvObject = static_cast(static_cast*>(this)); - return S_OK; - } - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE Release() { - return 1; - } - - ULONG STDMETHODCALLTYPE AddRef() { - return 2; - } - - HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) { - return E_NOTIMPL; - } - -protected: - DateTime _dateTime; -}; - -namespace Util { - - typedef LONG NTSTATUS, *PNTSTATUS; - typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); - inline RTL_OSVERSIONINFOW getRealOSVersion() { - HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); - if (hMod) { - RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); - if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = {0}; - rovi.dwOSVersionInfoSize = sizeof(rovi); - if (STATUS_SUCCESS == fxPtr(&rovi)) { - return rovi; - } - } - } - RTL_OSVERSIONINFOW rovi = {0}; - return rovi; - } - - inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); - DEBUG_MSG("Default executable path: " << path); - return (written > 0) ? S_OK : E_FAIL; - } - - inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link path: " << path); - } - return hr; - } - - inline HRESULT defaultShellLinkPath(_In_ std::wstring const& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link file path: " << path); - } - return hr; - } - - inline std::wstring parentDirectory(WCHAR* path, DWORD size) { - size_t lastSeparator = 0; - for (size_t i = 0; i < size; i++) { - if (path[i] == L'\\' || path[i] == L'/') { - lastSeparator = i; - } - } - return {path, lastSeparator}; - } - - inline PCWSTR AsString(_In_ ComPtr& xmlDocument) { - HSTRING xml; - ComPtr ser; - HRESULT hr = xmlDocument.As(&ser); - hr = ser->GetXml(&xml); - if (SUCCEEDED(hr)) { - return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); - } - return nullptr; - } - - inline PCWSTR AsString(_In_ HSTRING hstring) { - return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); - } - - inline HRESULT setNodeStringValue(_In_ std::wstring const& string, _Out_opt_ IXmlNode* node, _Out_ IXmlDocument* xml) { - ComPtr textNode; - HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); - if (SUCCEEDED(hr)) { - ComPtr stringNode; - hr = textNode.As(&stringNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = node->AppendChild(stringNode.Get(), &appendedChild); - } - } - return hr; - } - - template - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, - _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, - _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, - _In_ FunctorT&& markAsReadyForDeletionFunc) { - HRESULT hr = notification->add_Activated( - Callback, ITypedEventHandler>>( - [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) - { - ComPtr activatedEventArgs; - HRESULT hr = inspectable->QueryInterface(activatedEventArgs.GetAddressOf()); - if (SUCCEEDED(hr)) { - HSTRING argumentsHandle; - hr = activatedEventArgs->get_Arguments(&argumentsHandle); - if (SUCCEEDED(hr)) { - PCWSTR arguments = Util::AsString(argumentsHandle); - - if(wcscmp(arguments, L"action=reply") == 0) - { - ComPtr inputBoxActivatedEventArgs; - HRESULT hr2 = inspectable->QueryInterface(inputBoxActivatedEventArgs.GetAddressOf()); - - if(SUCCEEDED(hr2)) - { - ComPtr replyHandle; - inputBoxActivatedEventArgs->get_UserInput(&replyHandle); - - ComPtr<__FIMap_2_HSTRING_IInspectable> replyMap; - hr = replyHandle.As(&replyMap); - - if(SUCCEEDED(hr)) - { - IInspectable* propertySet; - hr = replyMap.Get()->Lookup(WinToastStringWrapper(L"textBox").Get(), &propertySet); - if (SUCCEEDED(hr)) - { - ComPtr propertyValue; - hr = propertySet->QueryInterface(IID_PPV_ARGS(&propertyValue)); - - if (SUCCEEDED(hr)) - { - // Successfully queried IPropertyValue, now extract the value - HSTRING userInput; - hr = propertyValue->GetString(&userInput); - - // Convert the HSTRING to a wide string - PCWSTR strValueW = AsString(userInput); - - // Convert the wide string to a STL std::string to pass it as parameter - // into the event. - std::wstring ogWstr(strValueW); - std::string str(ogWstr.length(), ' '); - std::copy(ogWstr.begin(), ogWstr.end(), str.begin()); - - if (SUCCEEDED(hr)) - { - eventHandler->toastActivated(str.c_str()); - return S_OK; - } - } - } - } - } - } - - if (arguments && *arguments) { - eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); - DllImporter::WindowsDeleteString(argumentsHandle); - markAsReadyForDeletionFunc(); - return S_OK; - } - DllImporter::WindowsDeleteString(argumentsHandle); - } - } - eventHandler->toastActivated(); - markAsReadyForDeletionFunc(); - return S_OK; - }) - .Get(), - &activatedToken); - - if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed( - Callback, ITypedEventHandler>>( - [eventHandler, expirationTime, markAsReadyForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) { - if (reason == ToastDismissalReason_UserCanceled && expirationTime && - InternalDateTime::Now() >= expirationTime) { - reason = ToastDismissalReason_TimedOut; - } - eventHandler->toastDismissed(static_cast(reason)); - } - markAsReadyForDeletionFunc(); - return S_OK; - }) - .Get(), - &dismissedToken); - if (SUCCEEDED(hr)) { - hr = notification->add_Failed( - Callback, ITypedEventHandler>>( - [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) { - eventHandler->toastFailed(); - markAsReadyForDeletionFunc(); - return S_OK; - }) - .Get(), - &failedToken); - } - } - return hr; - } - - inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { - ComPtr srcAttribute; - HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = srcAttribute.As(&node); - if (SUCCEEDED(hr)) { - ComPtr pNode; - hr = attributeMap->SetNamedItem(node.Get(), &pNode); - } - } - return hr; - } - - inline HRESULT createElement(_In_ IXmlDocument* xml, _In_ std::wstring const& root_node, _In_ std::wstring const& element_name, - _In_ std::vector const& attribute_names) { - ComPtr rootList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); - if (SUCCEEDED(hr)) { - ComPtr root; - hr = rootList->Item(0, &root); - if (SUCCEEDED(hr)) { - ComPtr audioElement; - hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); - if (SUCCEEDED(hr)) { - ComPtr audioNodeTmp; - hr = audioElement.As(&audioNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr audioNode; - hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = audioNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - for (auto const& it : attribute_names) { - hr = addAttribute(xml, it, attributes.Get()); - } - } - } - } - } - } - } - return hr; - } -} // namespace Util - -WinToast* WinToast::instance() { - thread_local static WinToast instance; - return &instance; -} - -WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { - if (!isCompatible()) { - DEBUG_MSG(L"Warning: Your system is not compatible with this library "); - } -} - -WinToast::~WinToast() { - clear(); - - if (_hasCoInitialized) { - CoUninitialize(); - } -} - -void WinToast::setAppName(_In_ std::wstring const& appName) { - _appName = appName; -} - -void WinToast::setAppUserModelId(_In_ std::wstring const& aumi) { - _aumi = aumi; - DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); -} - -void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { - _shortcutPolicy = shortcutPolicy; -} - -bool WinToast::isCompatible() { - DllImporter::initialize(); - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || - (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || - (DllImporter::WindowsDeleteString == nullptr)); -} - -bool WinToastLib::WinToast::isSupportingModernFeatures() { - constexpr auto MinimumSupportedVersion = 6; - return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; -} - -bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { - return Util::getRealOSVersion().dwBuildNumber >= 14393; -} - -std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, - _In_ std::wstring const& subProduct, _In_ std::wstring const& versionInformation) { - std::wstring aumi = companyName; - aumi += L"." + productName; - if (subProduct.length() > 0) { - aumi += L"." + subProduct; - if (versionInformation.length() > 0) { - aumi += L"." + versionInformation; - } - } - - if (aumi.length() > SCHAR_MAX) { - DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); - } - return aumi; -} - -std::wstring const& WinToast::strerror(WinToastError error) { - static const std::unordered_map Labels = { - {WinToastError::NoError, L"No error. The process was executed correctly" }, - {WinToastError::NotInitialized, L"The library has not been initialized" }, - {WinToastError::SystemNotSupported, L"The OS does not support WinToast" }, - {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app" }, - {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one" }, - {WinToastError::InvalidParameters, L"Invalid parameters, please double-check the AUMI or App Name" }, - {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, - {WinToastError::UnknownError, L"Unknown error" } - }; - - auto const iter = Labels.find(error); - assert(iter != Labels.end()); - return iter->second; -} - -enum WinToast::ShortcutResult WinToast::createShortcut() { - if (_aumi.empty() || _appName.empty()) { - DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); - return SHORTCUT_MISSING_PARAMETERS; - } - - if (!isCompatible()) { - DEBUG_MSG(L"Your OS is not compatible with this library! =("); - return SHORTCUT_INCOMPATIBLE_OS; - } - - if (!_hasCoInitialized) { - HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); - if (initHr != RPC_E_CHANGED_MODE) { - if (FAILED(initHr) && initHr != S_FALSE) { - DEBUG_MSG(L"Error on COM library initialization!"); - return SHORTCUT_COM_INIT_FAILURE; - } else { - _hasCoInitialized = true; - } - } - } - - bool wasChanged; - HRESULT hr = validateShellLinkHelper(wasChanged); - if (SUCCEEDED(hr)) { - return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; - } - - hr = createShellLinkHelper(); - return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; -} - -bool WinToast::initialize(_Out_opt_ WinToastError* error) { - _isInitialized = false; - setError(error, WinToastError::NoError); - - if (!isCompatible()) { - setError(error, WinToastError::SystemNotSupported); - DEBUG_MSG(L"Error: system not supported."); - return false; - } - - if (_aumi.empty() || _appName.empty()) { - setError(error, WinToastError::InvalidParameters); - DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); - return false; - } - - if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) { - if (createShortcut() < 0) { - setError(error, WinToastError::ShellLinkNotCreated); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - } - - if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { - setError(error, WinToastError::InvalidAppUserModelID); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - - _isInitialized = true; - return _isInitialized; -} - -bool WinToast::isInitialized() const { - return _isInitialized; -} - -std::wstring const& WinToast::appName() const { - return _appName; -} - -std::wstring const& WinToast::appUserModelId() const { - return _aumi; -} - -HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { - WCHAR path[MAX_PATH] = {L'\0'}; - Util::defaultShellLinkPath(_appName, path); - // Check if the file exist - DWORD attr = GetFileAttributesW(path); - if (attr >= 0xFFFFFFF) { - DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); - return E_FAIL; - } - - // Let's load the file as shell link to validate. - // - Create a shell link - // - Create a persistant file - // - Load the path as data for the persistant file - // - Read the property AUMI and validate with the current - // - Review if AUMI is equal. - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Load(path, STGM_READWRITE); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); - if (SUCCEEDED(hr)) { - WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); - wasChanged = false; - if (FAILED(hr) || _aumi != AUMI) { - if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { - // AUMI Changed for the same app, let's update the current value! =) - wasChanged = true; - PropVariantClear(&appIdPropVar); - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(path, TRUE); - } - } - } - } else { - // Not allowed to touch the shortcut to fix the AUMI - hr = E_FAIL; - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::createShellLinkHelper() { - if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { - return E_FAIL; - } - - WCHAR exePath[MAX_PATH]{L'\0'}; - WCHAR slPath[MAX_PATH]{L'\0'}; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - std::wstring exeDir = Util::parentDirectory(exePath, sizeof(exePath) / sizeof(exePath[0])); - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); - if (SUCCEEDED(hr)) { - hr = shellLink->SetArguments(L""); - if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exeDir.c_str()); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - } - return hr; -} - -INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { - std::shared_ptr handler(eventHandler); - setError(error, WinToastError::NoError); - INT64 id = -1; - if (!isInitialized()) { - setError(error, WinToastError::NotInitialized); - DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); - return id; - } - if (!handler) { - setError(error, WinToastError::InvalidHandler); - DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr."); - return id; - } - - ComPtr notificationManager; - HRESULT hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - ComPtr notifier; - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - if (SUCCEEDED(hr)) { - ComPtr notificationFactory; - hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); - if (SUCCEEDED(hr)) { - ComPtr xmlDocument; - hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); - if (SUCCEEDED(hr) && toast.isToastGeneric()) { - hr = setBindToastGenericHelper(xmlDocument.Get()); - } - if (SUCCEEDED(hr)) { - for (UINT32 i = 0, fieldsCount = static_cast(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { - hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); - } - - // Modern feature are supported Windows > Windows 10 - if (SUCCEEDED(hr) && isSupportingModernFeatures()) { - // Note that we do this *after* using toast.textFieldsCount() to - // iterate/fill the template's text fields, since we're adding yet another text field. - if (SUCCEEDED(hr) && !toast.attributionText().empty()) { - hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); - } - - std::array buf; - for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) { - _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i); - hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data()); - } - - if (SUCCEEDED(hr)) { - hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) - ? hr - : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); - } - - if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { - hr = addDurationHelper(xmlDocument.Get(), - (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); - } - - if(SUCCEEDED(hr) && toast.isInput()) { - hr = addInputHelper(xmlDocument.Get()); - } - - if (SUCCEEDED(hr)) { - hr = addScenarioHelper(xmlDocument.Get(), toast.scenario()); - } - - } else { - DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); - } - - if (SUCCEEDED(hr)) { - bool isWin10AnniversaryOrAbove = WinToast::isWin10AnniversaryOrHigher(); - bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; - hr = toast.hasImage() - ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) - : hr; - if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) { - hr = setHeroImageHelper(xmlDocument.Get(), toast.heroImagePath(), toast.isInlineHeroImage()); - } - if (SUCCEEDED(hr)) { - ComPtr notification; - hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); - if (SUCCEEDED(hr)) { - INT64 expiration = 0, relativeExpiration = toast.expiration(); - if (relativeExpiration > 0) { - InternalDateTime expirationDateTime(relativeExpiration); - expiration = expirationDateTime; - hr = notification->put_ExpirationTime(&expirationDateTime); - } - - EventRegistrationToken activatedToken, dismissedToken, failedToken; - - GUID guid; - HRESULT hrGuid = CoCreateGuid(&guid); - id = guid.Data1; - if (SUCCEEDED(hr) && SUCCEEDED(hrGuid)) { - hr = Util::setEventHandlers(notification.Get(), handler, expiration, activatedToken, dismissedToken, - failedToken, [this, id]() { markAsReadyForDeletion(id); }); - if (FAILED(hr)) { - setError(error, WinToastError::InvalidHandler); - } - } - - if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) { - _buffer.emplace(id, NotifyData(notification, activatedToken, dismissedToken, failedToken)); - DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); - hr = notifier->Show(notification.Get()); - if (FAILED(hr)) { - setError(error, WinToastError::NotDisplayed); - } - } - } - } - } - } - } - } - } - } - return FAILED(hr) ? -1 : id; -} - -ComPtr WinToast::notifier(_In_ bool* succeded) const { - ComPtr notificationManager; - ComPtr notifier; - HRESULT hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - } - *succeded = SUCCEEDED(hr); - return notifier; -} - -void WinToast::markAsReadyForDeletion(_In_ INT64 id) { - // Flush the buffer by removing all the toasts that are ready for deletion - for (auto it = _buffer.begin(); it != _buffer.end();) { - if (it->second.isReadyForDeletion()) { - it->second.RemoveTokens(); - it = _buffer.erase(it); - } else { - ++it; - } - } - - // Mark the toast as ready for deletion (if it exists) so that it will be removed from the buffer in the next iteration - auto const iter = _buffer.find(id); - if (iter != _buffer.end()) { - _buffer[id].markAsReadyForDeletion(); - } -} - -bool WinToast::hideToast(_In_ INT64 id) { - if (!isInitialized()) { - DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); - return false; - } - - auto iter = _buffer.find(id); - if (iter == _buffer.end()) { - return false; - } - - auto succeded = false; - auto notify = notifier(&succeded); - if (!succeded) { - return false; - } - - auto& notifyData = iter->second; - auto result = notify->Hide(notifyData.notification()); - if (FAILED(result)) { - DEBUG_MSG("Error when hiding the toast. Error code: " << result); - return false; - } - - notifyData.RemoveTokens(); - _buffer.erase(iter); - return SUCCEEDED(result); -} - -void WinToast::clear() { - auto succeded = false; - auto notify = notifier(&succeded); - if (!succeded) { - return; - } - - for (auto& data : _buffer) { - auto& notifyData = data.second; - notify->Hide(notifyData.notification()); - notifyData.RemoveTokens(); - } - _buffer.clear(); -} - -// -// Available as of Windows 10 Anniversary Update -// Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts -// -// NOTE: This will add a new text field, so be aware when iterating over -// the toast's text fields or getting a count of them. -// -HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { - Util::createElement(xml, L"binding", L"text", {L"placement"}); - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 nodeListLength; - hr = nodeList->get_Length(&nodeListLength); - if (SUCCEEDED(hr)) { - for (UINT32 i = 0; i < nodeListLength; i++) { - ComPtr textNode; - hr = nodeList->Item(i, &textNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = textNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); - if (FAILED(hr) || !editedNode) { - continue; - } - hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); - if (SUCCEEDED(hr)) { - return setTextFieldHelper(xml, text, i); - } - } - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); - } - } - } - } - return hr; -} - -HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), WinToastStringWrapper(scenario).Get()); - } - } - } - } - return hr; -} - -HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) -{ - std::vector attrbs; - attrbs.push_back(L"id"); - attrbs.push_back(L"type"); - attrbs.push_back(L"placeHolderContent"); - - std::vector attrbs2; - attrbs2.push_back(L"content"); - attrbs2.push_back(L"arguments"); - - Util::createElement(xml, L"toast", L"actions", {}); - - Util::createElement(xml, L"actions", L"input",attrbs); - Util::createElement(xml, L"actions", L"action",attrbs2); - - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"input").Get(), &nodeList); - if (SUCCEEDED(hr)) - { - ComPtr inputNode; - hr = nodeList->Item(0, &inputNode); - if (SUCCEEDED(hr)) - { - ComPtr toastElement; - hr = inputNode.As(&toastElement); - if(SUCCEEDED(hr)){ - toastElement->SetAttribute(WinToastStringWrapper(L"id").Get(), WinToastStringWrapper(L"textBox").Get()); - toastElement->SetAttribute(WinToastStringWrapper(L"type").Get(), WinToastStringWrapper(L"text").Get()); - hr = toastElement->SetAttribute(WinToastStringWrapper(L"placeHolderContent").Get(), WinToastStringWrapper(L"...").Get()); - } - } - } - - ComPtr nodeList2; - hr = xml->GetElementsByTagName(WinToastStringWrapper(L"action").Get(), &nodeList2); - if (SUCCEEDED(hr)) - { - ComPtr actionNode; - hr = nodeList2->Item(0, &actionNode); - if (SUCCEEDED(hr)) - { - ComPtr actionElement; - hr = actionNode.As(&actionElement); - if(SUCCEEDED(hr)){ - actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(L"Reply").Get()); - actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(L"action=reply").Get()); - actionElement->SetAttribute(WinToastStringWrapper(L"hint-inputId").Get(), WinToastStringWrapper(L"textBox").Get()); - } - } - } - return hr; -} - -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(pos, &node); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(text, node.Get(), xml); - } - } - return hr; -} - -HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); - } - } - } - } - return hr; -} - -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, - _In_ bool isCropHintCircle) { - assert(path.size() < MAX_PATH); - - wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); - if (SUCCEEDED(hr)) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - - ComPtr imageElement; - HRESULT hrImage = node.As(&imageElement); - if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"appLogoOverride").Get()); - if (SUCCEEDED(hr) && isCropHintCircle) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"hint-crop").Get(), WinToastStringWrapper(L"circle").Get()); - } - } - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(imagePath, editedNode.Get(), xml); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, - _In_opt_ WinToastTemplate::AudioOption option) { - std::vector attrs; - if (!path.empty()) { - attrs.push_back(L"src"); - } - if (option == WinToastTemplate::AudioOption::Loop) { - attrs.push_back(L"loop"); - } - if (option == WinToastTemplate::AudioOption::Silent) { - attrs.push_back(L"silent"); - } - Util::createElement(xml, L"toast", L"audio", attrs); - - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (!path.empty()) { - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(path, editedNode.Get(), xml); - } - } - } - - if (SUCCEEDED(hr)) { - switch (option) { - case WinToastTemplate::AudioOption::Loop: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - break; - case WinToastTemplate::AudioOption::Silent: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - default: - break; - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& content, _In_ std::wstring const& arguments) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr actionsNode; - if (length > 0) { - hr = nodeList->Item(0, &actionsNode); - } else { - hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), - WinToastStringWrapper(L"ToastGeneric").Get()); - } - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), - WinToastStringWrapper(L"long").Get()); - } - if (SUCCEEDED(hr)) { - ComPtr actionsElement; - hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); - if (SUCCEEDED(hr)) { - hr = actionsElement.As(&actionsNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); - } - } - } - } - } - } - } - if (SUCCEEDED(hr)) { - ComPtr actionElement; - hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) { - hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); - } - if (SUCCEEDED(hr)) { - hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); - } - if (SUCCEEDED(hr)) { - ComPtr actionNode; - hr = actionElement.As(&actionNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr bindingNode; - if (length > 0) { - hr = nodeList->Item(0, &bindingNode); - } - if (SUCCEEDED(hr)) { - ComPtr imageElement; - hr = xml->CreateElement(WinToastStringWrapper(L"image").Get(), &imageElement); - if (SUCCEEDED(hr) && isInlineImage == false) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"hero").Get()); - } - if (SUCCEEDED(hr)) { - hr = imageElement->SetAttribute(WinToastStringWrapper(L"src").Get(), WinToastStringWrapper(path).Get()); - } - if (SUCCEEDED(hr)) { - ComPtr actionNode; - hr = imageElement.As(&actionNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = bindingNode->AppendChild(actionNode.Get(), &appendedChild); - } - } - } - } - } - return hr; -} - -void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value) { - if (error) { - *error = value; - } -} - -WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { - constexpr static std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[type], L""); -} - -WinToastTemplate::~WinToastTemplate() { - _textFields.clear(); -} - -void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { - auto const position = static_cast(pos); - if (position >= _textFields.size()) { - DEBUG_MSG("The selected template type supports only " << _textFields.size() << " text lines"); - return; - } - _textFields[position] = txt; -} - -void WinToastTemplate::setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint) { - _imagePath = imgPath; - _cropHint = cropHint; -} - -void WinToastTemplate::setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage) { - _heroImagePath = imgPath; - _inlineHeroImage = inlineImage; -} - -void WinToastTemplate::setAudioPath(_In_ std::wstring const& audioPath) { - _audioPath = audioPath; -} - -void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { - static const std::unordered_map Files = { - {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default" }, - {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM" }, - {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail" }, - {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder" }, - {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS" }, - {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm" }, - {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2" }, - {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3" }, - {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4" }, - {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5" }, - {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6" }, - {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7" }, - {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8" }, - {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9" }, - {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, - {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call" }, - {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1" }, - {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2" }, - {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3" }, - {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4" }, - {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5" }, - {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6" }, - {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7" }, - {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8" }, - {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9" }, - {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10" }, - }; - auto const iter = Files.find(file); - assert(iter != Files.end()); - _audioPath = iter->second; -} - -void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { - _audioOption = audioOption; -} - -void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::FirstLine); -} - -void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::SecondLine); -} - -void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { - setTextField(text, WinToastTemplate::ThirdLine); -} - -void WinToastTemplate::setDuration(_In_ Duration duration) { - _duration = duration; -} - -void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { - _expiration = millisecondsFromNow; -} - -void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { - switch (scenario) { - case Scenario::Default: - _scenario = L"Default"; - break; - case Scenario::Alarm: - _scenario = L"Alarm"; - break; - case Scenario::IncomingCall: - _scenario = L"IncomingCall"; - break; - case Scenario::Reminder: - _scenario = L"Reminder"; - break; - } -} - -void WinToastTemplate::setAttributionText(_In_ std::wstring const& attributionText) { - _attributionText = attributionText; -} - -void WinToastTemplate::addAction(_In_ std::wstring const& label) { - _actions.push_back(label); -} - -void WinToastTemplate::addInput() -{ - _hasInput = true; -} - -std::size_t WinToastTemplate::textFieldsCount() const { - return _textFields.size(); -} - -std::size_t WinToastTemplate::actionsCount() const { - return _actions.size(); -} - -bool WinToastTemplate::hasImage() const { - return _type < WinToastTemplateType::Text01; -} - -bool WinToastTemplate::hasHeroImage() const { - return hasImage() && !_heroImagePath.empty(); -} - -std::vector const& WinToastTemplate::textFields() const { - return _textFields; -} - -std::wstring const& WinToastTemplate::textField(_In_ TextField pos) const { - auto const position = static_cast(pos); - assert(position < _textFields.size()); - return _textFields[position]; -} - -std::wstring const& WinToastTemplate::actionLabel(_In_ std::size_t position) const { - assert(position < _actions.size()); - return _actions[position]; -} - -std::wstring const& WinToastTemplate::imagePath() const { - return _imagePath; -} - -std::wstring const& WinToastTemplate::heroImagePath() const { - return _heroImagePath; -} - -std::wstring const& WinToastTemplate::audioPath() const { - return _audioPath; -} - -std::wstring const& WinToastTemplate::attributionText() const { - return _attributionText; -} - -std::wstring const& WinToastLib::WinToastTemplate::scenario() const { - return _scenario; -} - -INT64 WinToastTemplate::expiration() const { - return _expiration; -} - -WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { - return _type; -} - -WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { - return _audioOption; -} - -WinToastTemplate::Duration WinToastTemplate::duration() const { - return _duration; -} - -bool WinToastTemplate::isToastGeneric() const { - return hasHeroImage() || _cropHint == WinToastTemplate::Circle; -} - -bool WinToastTemplate::isInlineHeroImage() const { - return _inlineHeroImage; -} - -bool WinToastTemplate::isCropHintCircle() const { - return _cropHint == CropHint::Circle; -} - -bool WinToastTemplate::isInput() const{ - return _hasInput; -} diff --git a/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.h b/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.h deleted file mode 100644 index d68c3b895c..0000000000 --- a/Client/qtTeamTalk/3rdparty/WinToast/wintoastlib.h +++ /dev/null @@ -1,316 +0,0 @@ -/** - * MIT License - * - * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef WINTOASTLIB_H -#define WINTOASTLIB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Microsoft::WRL; -using namespace ABI::Windows::Data::Xml::Dom; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::UI::Notifications; -using namespace Windows::Foundation; - -namespace WinToastLib { - - class IWinToastHandler { - public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, - ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut - }; - - virtual ~IWinToastHandler() = default; - virtual void toastActivated() const = 0; - virtual void toastActivated(int actionIndex) const = 0; - virtual void toastActivated(const char* response) const = 0; - virtual void toastDismissed(WinToastDismissalReason state) const = 0; - virtual void toastFailed() const = 0; - }; - - class WinToastTemplate { - public: - enum class Scenario { Default, Alarm, IncomingCall, Reminder }; - enum Duration { System, Short, Long }; - enum AudioOption { Default = 0, Silent, Loop }; - enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - - enum WinToastTemplateType { - ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, - ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, - ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, - ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, - Text01 = ToastTemplateType::ToastTemplateType_ToastText01, - Text02 = ToastTemplateType::ToastTemplateType_ToastText02, - Text03 = ToastTemplateType::ToastTemplateType_ToastText03, - Text04 = ToastTemplateType::ToastTemplateType_ToastText04 - }; - - enum AudioSystemFile { - DefaultSound, - IM, - Mail, - Reminder, - SMS, - Alarm, - Alarm2, - Alarm3, - Alarm4, - Alarm5, - Alarm6, - Alarm7, - Alarm8, - Alarm9, - Alarm10, - Call, - Call1, - Call2, - Call3, - Call4, - Call5, - Call6, - Call7, - Call8, - Call9, - Call10, - }; - - enum CropHint { - Square, - Circle, - }; - - WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); - ~WinToastTemplate(); - - void setFirstLine(_In_ std::wstring const& text); - void setSecondLine(_In_ std::wstring const& text); - void setThirdLine(_In_ std::wstring const& text); - void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); - void setAttributionText(_In_ std::wstring const& attributionText); - void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); - void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); - void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); - void setAudioPath(_In_ std::wstring const& audioPath); - void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); - void setDuration(_In_ Duration duration); - void setExpiration(_In_ INT64 millisecondsFromNow); - void setScenario(_In_ Scenario scenario); - void addAction(_In_ std::wstring const& label); - void addInput(); - - std::size_t textFieldsCount() const; - std::size_t actionsCount() const; - bool hasImage() const; - bool hasHeroImage() const; - std::vector const& textFields() const; - std::wstring const& textField(_In_ TextField pos) const; - std::wstring const& actionLabel(_In_ std::size_t pos) const; - std::wstring const& imagePath() const; - std::wstring const& heroImagePath() const; - std::wstring const& audioPath() const; - std::wstring const& attributionText() const; - std::wstring const& scenario() const; - INT64 expiration() const; - WinToastTemplateType type() const; - WinToastTemplate::AudioOption audioOption() const; - Duration duration() const; - bool isToastGeneric() const; - bool isInlineHeroImage() const; - bool isCropHintCircle() const; - bool isInput() const; - - private: - bool _hasInput{false}; - - std::vector _textFields{}; - std::vector _actions{}; - std::wstring _imagePath{}; - std::wstring _heroImagePath{}; - bool _inlineHeroImage{false}; - std::wstring _audioPath{}; - std::wstring _attributionText{}; - std::wstring _scenario{L"Default"}; - INT64 _expiration{0}; - AudioOption _audioOption{WinToastTemplate::AudioOption::Default}; - WinToastTemplateType _type{WinToastTemplateType::Text01}; - Duration _duration{Duration::System}; - CropHint _cropHint{CropHint::Square}; - }; - - class WinToast { - public: - enum WinToastError { - NoError = 0, - NotInitialized, - SystemNotSupported, - ShellLinkNotCreated, - InvalidAppUserModelID, - InvalidParameters, - InvalidHandler, - NotDisplayed, - UnknownError - }; - - enum ShortcutResult { - SHORTCUT_UNCHANGED = 0, - SHORTCUT_WAS_CHANGED = 1, - SHORTCUT_WAS_CREATED = 2, - - SHORTCUT_MISSING_PARAMETERS = -1, - SHORTCUT_INCOMPATIBLE_OS = -2, - SHORTCUT_COM_INIT_FAILURE = -3, - SHORTCUT_CREATE_FAILED = -4 - }; - - enum ShortcutPolicy { - /* Don't check, create, or modify a shortcut. */ - SHORTCUT_POLICY_IGNORE = 0, - /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ - SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, - /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ - SHORTCUT_POLICY_REQUIRE_CREATE = 2, - }; - - WinToast(void); - virtual ~WinToast(); - static WinToast* instance(); - static bool isCompatible(); - static bool isSupportingModernFeatures(); - static bool isWin10AnniversaryOrHigher(); - static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, - _In_ std::wstring const& subProduct = std::wstring(), - _In_ std::wstring const& versionInformation = std::wstring()); - static std::wstring const& strerror(_In_ WinToastError error); - virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); - virtual bool isInitialized() const; - virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, - _Out_opt_ WinToastError* error = nullptr); - virtual void clear(); - virtual enum ShortcutResult createShortcut(); - - std::wstring const& appName() const; - std::wstring const& appUserModelId() const; - void setAppUserModelId(_In_ std::wstring const& aumi); - void setAppName(_In_ std::wstring const& appName); - void setShortcutPolicy(_In_ ShortcutPolicy policy); - - protected: - struct NotifyData { - NotifyData(){}; - NotifyData(_In_ ComPtr notify, _In_ EventRegistrationToken activatedToken, - _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : - _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} - - ~NotifyData() { - RemoveTokens(); - } - - void RemoveTokens() { - if (!_readyForDeletion) { - return; - } - - if (_previouslyTokenRemoved) { - return; - } - - if (!_notify.Get()) { - return; - } - - _notify->remove_Activated(_activatedToken); - _notify->remove_Dismissed(_dismissedToken); - _notify->remove_Failed(_failedToken); - _previouslyTokenRemoved = true; - } - - void markAsReadyForDeletion() { - _readyForDeletion = true; - } - - bool isReadyForDeletion() const { - return _readyForDeletion; - } - - IToastNotification* notification() { - return _notify.Get(); - } - - private: - ComPtr _notify{nullptr}; - EventRegistrationToken _activatedToken{}; - EventRegistrationToken _dismissedToken{}; - EventRegistrationToken _failedToken{}; - bool _readyForDeletion{false}; - bool _previouslyTokenRemoved{false}; - }; - - bool _isInitialized{false}; - bool _hasCoInitialized{false}; - ShortcutPolicy _shortcutPolicy{SHORTCUT_POLICY_REQUIRE_CREATE}; - std::wstring _appName{}; - std::wstring _aumi{}; - std::map _buffer{}; - - void markAsReadyForDeletion(_In_ INT64 id); - HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); - HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); - HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); - HRESULT - setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, - _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); - HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); - HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); - HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); - HRESULT addInputHelper(_In_ IXmlDocument* xml); - ComPtr notifier(_In_ bool* succeded) const; - void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); - }; -} // namespace WinToastLib -#endif // WINTOASTLIB_H diff --git a/Client/qtTeamTalk/CMakeLists.txt b/Client/qtTeamTalk/CMakeLists.txt index 7f8549db4d..855cf1b16c 100644 --- a/Client/qtTeamTalk/CMakeLists.txt +++ b/Client/qtTeamTalk/CMakeLists.txt @@ -27,10 +27,14 @@ endif() find_package (Qt6 COMPONENTS Widgets Xml Network Multimedia TextToSpeech) if (WIN32) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/WinToast) - set(WINTOAST_SOURCES - 3rdparty/WinToast/wintoastlib.cpp 3rdparty/WinToast/wintoastlib.h + include(FetchContent) + FetchContent_Declare( + WinToast + GIT_REPOSITORY https://github.com/mohabouje/WinToast.git + GIT_TAG v1.3.2 ) + set(WINTOASTLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(WinToast) endif() if (Qt5_FOUND OR Qt6_FOUND) @@ -125,9 +129,6 @@ if (Qt5_FOUND OR Qt6_FOUND) resources.qrc mainwindow.rc ) -if (WIN32) - list(APPEND QTTEAMTALK_SOURCES ${WINTOAST_SOURCES}) -endif() if (Qt6_FOUND) set (TEAMTALK_LINK_FLAGS Qt6::Widgets Qt6::Xml Qt6::Network Qt6::Multimedia Qt6::TextToSpeech) @@ -240,6 +241,10 @@ endif() add_executable (${TEAMTALK_TARGET} ${QTTEAMTALK_SOURCES}) endif() + if (WIN32) + target_link_libraries (${TEAMTALK_TARGET} WinToast) + endif() + if (BUILD_TEAMTALK_LIBRARY_DLL) target_include_directories (${TEAMTALK_TARGET} PUBLIC ./) target_link_libraries (${TEAMTALK_TARGET} ${TEAMTALK_LINK_FLAGS} TeamTalk5DLL) diff --git a/Client/qtTeamTalk/utilui.cpp b/Client/qtTeamTalk/utilui.cpp index bac9e4c8c5..a0fbcc88e1 100644 --- a/Client/qtTeamTalk/utilui.cpp +++ b/Client/qtTeamTalk/utilui.cpp @@ -774,7 +774,7 @@ QString PasswordDialog::getPassword() const } #if defined(Q_OS_WIN) -#include "3rdparty/WinToast/wintoastlib.h" +#include #include using namespace WinToastLib; @@ -801,8 +801,8 @@ class CustomHandler : public IWinToastHandler { std::wcout << L"The user clicked on button #" << actionIndex << L" in this toast" << std::endl; } - void toastActivated(const char* arguments) const override { - std::wcout << L"The user clicked in this toast with arguments: " << arguments << std::endl; + void toastActivated(std::wstring response) const override { + std::wcout << L"The user clicked in this toast with response: " << response << std::endl; } void toastFailed() const override {