diff --git a/src/snoretoasts.cpp b/src/snoretoasts.cpp index 0cc7556..bb87404 100644 --- a/src/snoretoasts.cpp +++ b/src/snoretoasts.cpp @@ -1,708 +1,586 @@ /* SnoreToast is capable to invoke Windows 8 toast notifications. Copyright (C) 2013-2019 Hannah von Reth SnoreToast is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SnoreToast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SnoreToast. If not, see . */ #include "snoretoasts.h" #include "toasteventhandler.h" #include "linkhelper.h" #include "utils.h" #include "stringreferencewrapper.h" #include #include using namespace Microsoft::WRL; using namespace ABI::Windows::UI; using namespace ABI::Windows::UI::Notifications; using namespace ABI::Windows::Data::Xml::Dom; using namespace Windows::Foundation; class SnoreToastsPrivate { public: SnoreToastsPrivate(SnoreToasts *parent, const std::wstring &appID) : m_parent(parent), m_appID(appID), m_id(std::to_wstring(GetCurrentProcessId())) { HRESULT hr = GetActivationFactory( - StringReferenceWrapper( - RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) - .Get(), + StringRef(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &m_toastManager); if (!SUCCEEDED(hr)) { std::wcerr << L"SnoreToasts: Failed to register com Factory, please make sure you " L"correctly initialised with RO_INIT_MULTITHREADED" << std::endl; m_action = SnoreToastActions::Actions::Error; } } SnoreToasts *m_parent; std::wstring m_appID; std::filesystem::path m_pipeName; std::filesystem::path m_application; std::wstring m_title; std::wstring m_body; std::filesystem::path m_image; std::wstring m_sound = L"Notification.Default"; std::wstring m_id; std::wstring m_buttons; bool m_silent = false; bool m_wait = false; bool m_textbox = false; SnoreToastActions::Actions m_action = SnoreToastActions::Actions::Clicked; ComPtr m_toastXml; - ComPtr m_toastManager; - ComPtr m_notifier; - ComPtr m_notification; + ComPtr m_toastManager; + ComPtr m_notifier; + ComPtr m_notification; ComPtr m_eventHanlder; static HANDLE ctoastEvent() { static HANDLE _event = [] { std::wstringstream eventName; eventName << L"ToastActivationEvent" << GetCurrentProcessId(); return CreateEvent(nullptr, true, false, eventName.str().c_str()); }(); return _event; } ComPtr getHistory() { - // TODO: refactor - - HRESULT hr = S_OK; ComPtr toastStatics2; - hr = m_toastManager.As(&toastStatics2); - if (SUCCEEDED(hr)) { + if (Utils::checkResult(m_toastManager.As(&toastStatics2))) { ComPtr nativeHistory; - hr = toastStatics2->get_History(&nativeHistory); - if (SUCCEEDED(hr)) { - return nativeHistory; - } + Utils::checkResult(toastStatics2->get_History(&nativeHistory)); + return nativeHistory; } return {}; } }; SnoreToasts::SnoreToasts(const std::wstring &appID) : d(new SnoreToastsPrivate(this, appID)) { Utils::registerActivator(); } SnoreToasts::~SnoreToasts() { Utils::unregisterActivator(); delete d; } -void SnoreToasts::displayToast(const std::wstring &title, const std::wstring &body, - const std::filesystem::path &image, bool wait) +HRESULT SnoreToasts::displayToast(const std::wstring &title, const std::wstring &body, + const std::filesystem::path &image, bool wait) { - HRESULT hr = S_OK; + // asume that we fail + d->m_action = SnoreToastActions::Actions::Error; + d->m_title = title; d->m_body = body; d->m_image = image; d->m_wait = wait; if (!d->m_image.empty()) { - hr = d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastImageAndText02, - &d->m_toastXml); + ReturnOnErrorHr(d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastImageAndText02, + &d->m_toastXml)); } else { - hr = d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastText02, &d->m_toastXml); + ReturnOnErrorHr(d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastText02, + &d->m_toastXml)); } + ComPtr rootList; + ReturnOnErrorHr(d->m_toastXml->GetElementsByTagName(StringRef(L"toast").Get(), &rootList)); + + ComPtr root; + ReturnOnErrorHr(rootList->Item(0, &root)); + ComPtr rootAttributes; + ReturnOnErrorHr(root->get_Attributes(&rootAttributes)); + + const auto data = formatAction(SnoreToastActions::Actions::Clicked); + ReturnOnErrorHr(addAttribute(L"launch", rootAttributes.Get(), data)); + // Adding buttons + if (!d->m_buttons.empty()) { + setButtons(root); + } else if (d->m_textbox) { + setTextBox(root); + } + ComPtr audioElement; + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"audio").Get(), &audioElement)); - if (SUCCEEDED(hr)) { - ComPtr rootList; - hr = d->m_toastXml->GetElementsByTagName(StringReferenceWrapper(L"toast").Get(), &rootList); + ComPtr audioNodeTmp; + ReturnOnErrorHr(audioElement.As(&audioNodeTmp)); - if (SUCCEEDED(hr)) { - ComPtr root; - hr = rootList->Item(0, &root); - - ComPtr rootAttributes; - hr = root->get_Attributes(&rootAttributes); - if (SUCCEEDED(hr)) { - const auto data = formatAction(SnoreToastActions::Actions::Clicked); - hr = addAttribute(L"launch", rootAttributes.Get(), data); - } - // Adding buttons - if (!d->m_buttons.empty()) { - setButtons(root); - } else if (d->m_textbox) { - setTextBox(root); - } - if (SUCCEEDED(hr)) { - ComPtr audioElement; - hr = d->m_toastXml->CreateElement(StringReferenceWrapper(L"audio").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)) { - hr = addAttribute(L"src", attributes.Get()); - if (SUCCEEDED(hr)) { - addAttribute(L"silent", attributes.Get()); - } - } - } - } - } - } - } - } + ComPtr audioNode; + ReturnOnErrorHr(root->AppendChild(audioNodeTmp.Get(), &audioNode)); + + ComPtr attributes; + ReturnOnErrorHr(audioNode->get_Attributes(&attributes)); + ReturnOnErrorHr(addAttribute(L"src", attributes.Get())); + ReturnOnErrorHr(addAttribute(L"silent", attributes.Get())); // printXML(); - if (SUCCEEDED(hr)) { - if (!d->m_image.empty()) { - hr = setImage(); - } - if (SUCCEEDED(hr)) { - hr = setSound(); - if (SUCCEEDED(hr)) { - hr = setTextValues(); - if (SUCCEEDED(hr)) { - printXML(); - hr = createToast(); - } - } - } + if (!d->m_image.empty()) { + ReturnOnErrorHr(setImage()); } - d->m_action = - SUCCEEDED(hr) ? SnoreToastActions::Actions::Clicked : SnoreToastActions::Actions::Error; + ReturnOnErrorHr(setSound()); + + ReturnOnErrorHr(setTextValues()); + + printXML(); + ReturnOnErrorHr(createToast()); + d->m_action = SnoreToastActions::Actions::Clicked; + return S_OK; } SnoreToastActions::Actions SnoreToasts::userAction() { if (d->m_eventHanlder.Get()) { HANDLE event = d->m_eventHanlder.Get()->event(); WaitForSingleObject(event, INFINITE); d->m_action = d->m_eventHanlder.Get()->userAction(); if (d->m_action == SnoreToastActions::Actions::Hidden) { d->m_notifier->Hide(d->m_notification.Get()); tLog << L"The application hid the toast using ToastNotifier.hide()"; } CloseHandle(event); } return d->m_action; } bool SnoreToasts::closeNotification() { std::wstringstream eventName; eventName << L"ToastEvent" << d->m_id; HANDLE event = OpenEventW(EVENT_ALL_ACCESS, FALSE, eventName.str().c_str()); if (event) { SetEvent(event); return true; } - auto history = d->getHistory(); - if (history.Get()) { - HRESULT hr = S_OK; - hr = history->RemoveGroupedTagWithId(StringReferenceWrapper(d->m_id).Get(), - StringReferenceWrapper(L"SnoreToast").Get(), - StringReferenceWrapper(d->m_appID).Get()); - if (SUCCEEDED(hr)) { + + if (auto history = d->getHistory()) { + if (Utils::checkResult(history->RemoveGroupedTagWithId(StringRef(d->m_id).Get(), + StringRef(L"SnoreToast").Get(), + StringRef(d->m_appID).Get()))) { return true; } - tLog << hr; } tLog << "Notification " << d->m_id << " does not exist"; return false; } void SnoreToasts::setSound(const std::wstring &soundFile) { d->m_sound = soundFile; } void SnoreToasts::setSilent(bool silent) { d->m_silent = silent; } void SnoreToasts::setId(const std::wstring &id) { if (!id.empty()) { d->m_id = id; } } std::wstring SnoreToasts::id() const { return d->m_id; } void SnoreToasts::setButtons(const std::wstring &buttons) { d->m_buttons = buttons; } void SnoreToasts::setTextBoxEnabled(bool textBoxEnabled) { d->m_textbox = textBoxEnabled; } // Set the value of the "src" attribute of the "image" node HRESULT SnoreToasts::setImage() { - HRESULT hr = S_OK; - ComPtr nodeList; - hr = d->m_toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr imageNode; - hr = nodeList->Item(0, &imageNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; + ReturnOnErrorHr(d->m_toastXml->GetElementsByTagName(StringRef(L"image").Get(), &nodeList)); - hr = imageNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr srcAttribute; + ComPtr imageNode; + ReturnOnErrorHr(nodeList->Item(0, &imageNode)); - hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute); - if (SUCCEEDED(hr)) { - hr = setNodeValueString(StringReferenceWrapper(d->m_image).Get(), - srcAttribute.Get()); - } - } - } - } - return hr; + ComPtr attributes; + ReturnOnErrorHr(imageNode->get_Attributes(&attributes)); + + ComPtr srcAttribute; + ReturnOnErrorHr(attributes->GetNamedItem(StringRef(L"src").Get(), &srcAttribute)); + return setNodeValueString(StringRef(d->m_image).Get(), srcAttribute.Get()); } HRESULT SnoreToasts::setSound() { - HRESULT hr = S_OK; ComPtr nodeList; - hr = d->m_toastXml->GetElementsByTagName(StringReferenceWrapper(L"audio").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr audioNode; - hr = nodeList->Item(0, &audioNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - - hr = audioNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr srcAttribute; - - hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute); - if (SUCCEEDED(hr)) { - std::wstring sound; - if (d->m_sound.find(L"ms-winsoundevent:") == std::wstring::npos) { - sound = L"ms-winsoundevent:"; - sound.append(d->m_sound); - } else { - sound = d->m_sound; - } - - hr = setNodeValueString(StringReferenceWrapper(sound).Get(), - srcAttribute.Get()); - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(StringReferenceWrapper(L"silent").Get(), - &srcAttribute); - if (SUCCEEDED(hr)) { - hr = setNodeValueString( - StringReferenceWrapper(d->m_silent ? L"true" : L"false").Get(), - srcAttribute.Get()); - } - } - } - } - } + ReturnOnErrorHr(d->m_toastXml->GetElementsByTagName(StringRef(L"audio").Get(), &nodeList)); + + ComPtr audioNode; + ReturnOnErrorHr(nodeList->Item(0, &audioNode)); + + ComPtr attributes; + + ReturnOnErrorHr(audioNode->get_Attributes(&attributes)); + ComPtr srcAttribute; + + ReturnOnErrorHr(attributes->GetNamedItem(StringRef(L"src").Get(), &srcAttribute)); + std::wstring sound; + if (d->m_sound.find(L"ms-winsoundevent:") == std::wstring::npos) { + sound = L"ms-winsoundevent:"; + sound.append(d->m_sound); + } else { + sound = d->m_sound; } - return hr; + + ReturnOnErrorHr(setNodeValueString(StringRef(sound).Get(), srcAttribute.Get())); + ReturnOnErrorHr(attributes->GetNamedItem(StringRef(L"silent").Get(), &srcAttribute)); + return setNodeValueString(StringRef(d->m_silent ? L"true" : L"false").Get(), + srcAttribute.Get()); } // Set the values of each of the text nodes HRESULT SnoreToasts::setTextValues() { - HRESULT hr = S_OK; - if (SUCCEEDED(hr)) { - ComPtr nodeList; - hr = d->m_toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - // create the title - ComPtr textNode; - hr = nodeList->Item(0, &textNode); - if (SUCCEEDED(hr)) { - hr = setNodeValueString(StringReferenceWrapper(d->m_title).Get(), textNode.Get()); - if (SUCCEEDED(hr)) { - - hr = nodeList->Item(1, &textNode); - if (SUCCEEDED(hr)) { - hr = setNodeValueString(StringReferenceWrapper(d->m_body).Get(), - textNode.Get()); - } - } - } - } - } - return hr; + ComPtr nodeList; + ReturnOnErrorHr(d->m_toastXml->GetElementsByTagName(StringRef(L"text").Get(), &nodeList)); + // create the title + ComPtr textNode; + ReturnOnErrorHr(nodeList->Item(0, &textNode)); + ReturnOnErrorHr(setNodeValueString(StringRef(d->m_title).Get(), textNode.Get())); + ReturnOnErrorHr(nodeList->Item(1, &textNode)); + return setNodeValueString(StringRef(d->m_body).Get(), textNode.Get()); } HRESULT SnoreToasts::setButtons(ComPtr root) { ComPtr actionsElement; - HRESULT hr = - d->m_toastXml->CreateElement(StringReferenceWrapper(L"actions").Get(), &actionsElement); - if (SUCCEEDED(hr)) { - ComPtr actionsNodeTmp; - hr = actionsElement.As(&actionsNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr actionsNode; - root->AppendChild(actionsNodeTmp.Get(), &actionsNode); + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"actions").Get(), &actionsElement)); - std::wstring buttonText; - std::wstringstream wss(d->m_buttons); - while (std::getline(wss, buttonText, L';')) { - hr &= createNewActionButton(actionsNode, buttonText); - } - } - } + ComPtr actionsNodeTmp; + ReturnOnErrorHr(actionsElement.As(&actionsNodeTmp)); - return hr; + ComPtr actionsNode; + ReturnOnErrorHr(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); + + std::wstring buttonText; + std::wstringstream wss(d->m_buttons); + while (std::getline(wss, buttonText, L';')) { + ReturnOnErrorHr(createNewActionButton(actionsNode, buttonText)); + } + return S_OK; } HRESULT SnoreToasts::setTextBox(ComPtr root) { ComPtr actionsElement; - HRESULT hr = - d->m_toastXml->CreateElement(StringReferenceWrapper(L"actions").Get(), &actionsElement); - if (SUCCEEDED(hr)) { - ComPtr actionsNodeTmp; - hr = actionsElement.As(&actionsNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr actionsNode; - root->AppendChild(actionsNodeTmp.Get(), &actionsNode); - - ComPtr inputElement; - HRESULT hr = d->m_toastXml->CreateElement(StringReferenceWrapper(L"input").Get(), - &inputElement); - if (SUCCEEDED(hr)) { - ComPtr inputNodeTmp; - hr = inputElement.As(&inputNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr inputNode; - actionsNode->AppendChild(inputNodeTmp.Get(), &inputNode); - ComPtr inputAttributes; - hr = inputNode->get_Attributes(&inputAttributes); - if (SUCCEEDED(hr)) { - hr &= addAttribute(L"id", inputAttributes.Get(), L"textBox"); - hr &= addAttribute(L"type", inputAttributes.Get(), L"text"); - hr &= addAttribute(L"placeHolderContent", inputAttributes.Get(), - L"Type a reply"); - } - } - ComPtr actionElement; - HRESULT hr = d->m_toastXml->CreateElement(StringReferenceWrapper(L"action").Get(), - &actionElement); - if (SUCCEEDED(hr)) { - ComPtr actionNodeTmp; - hr = actionElement.As(&actionNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr actionNode; - actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode); - ComPtr actionAttributes; - hr = actionNode->get_Attributes(&actionAttributes); - if (SUCCEEDED(hr)) { - hr &= addAttribute(L"content", actionAttributes.Get(), L"Send"); - const auto data = - formatAction(SnoreToastActions::Actions::ButtonClicked); - hr &= addAttribute(L"arguments", actionAttributes.Get(), data); - hr &= addAttribute(L"hint-inputId", actionAttributes.Get(), L"textBox"); - } - } - } - } - } - } + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"actions").Get(), &actionsElement)); - return hr; + ComPtr actionsNodeTmp; + ReturnOnErrorHr(actionsElement.As(&actionsNodeTmp)); + + ComPtr actionsNode; + ReturnOnErrorHr(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); + + ComPtr inputElement; + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"input").Get(), &inputElement)); + + ComPtr inputNodeTmp; + ReturnOnErrorHr(inputElement.As(&inputNodeTmp)); + + ComPtr inputNode; + ReturnOnErrorHr(actionsNode->AppendChild(inputNodeTmp.Get(), &inputNode)); + + ComPtr inputAttributes; + ReturnOnErrorHr(inputNode->get_Attributes(&inputAttributes)); + + ReturnOnErrorHr(addAttribute(L"id", inputAttributes.Get(), L"textBox")); + ReturnOnErrorHr(addAttribute(L"type", inputAttributes.Get(), L"text")); + ReturnOnErrorHr(addAttribute(L"placeHolderContent", inputAttributes.Get(), L"Type a reply")); + + ComPtr actionElement; + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"action").Get(), &actionElement)); + + ComPtr actionNodeTmp; + ReturnOnErrorHr(actionElement.As(&actionNodeTmp)); + + ComPtr actionNode; + ReturnOnErrorHr(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); + + ComPtr actionAttributes; + ReturnOnErrorHr(actionNode->get_Attributes(&actionAttributes)); + + ReturnOnErrorHr(addAttribute(L"content", actionAttributes.Get(), L"Send")); + + const auto data = formatAction(SnoreToastActions::Actions::ButtonClicked); + + ReturnOnErrorHr(addAttribute(L"arguments", actionAttributes.Get(), data)); + return addAttribute(L"hint-inputId", actionAttributes.Get(), L"textBox"); } HRESULT SnoreToasts::setEventHandler(ComPtr toast) { // Register the event handlers EventRegistrationToken activatedToken, dismissedToken, failedToken; ComPtr eventHandler(new ToastEventHandler(*this)); - HRESULT hr = toast->add_Activated(eventHandler.Get(), &activatedToken); - if (SUCCEEDED(hr)) { - hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); - if (SUCCEEDED(hr)) { - hr = toast->add_Failed(eventHandler.Get(), &failedToken); - if (SUCCEEDED(hr)) { - d->m_eventHanlder = eventHandler; - } - } - } - return hr; + ReturnOnErrorHr(toast->add_Activated(eventHandler.Get(), &activatedToken)); + ReturnOnErrorHr(toast->add_Dismissed(eventHandler.Get(), &dismissedToken)); + ReturnOnErrorHr(toast->add_Failed(eventHandler.Get(), &failedToken)); + d->m_eventHanlder = eventHandler; + return S_OK; } HRESULT SnoreToasts::setNodeValueString(const HSTRING &inputString, IXmlNode *node) { ComPtr inputText; + ReturnOnErrorHr(d->m_toastXml->CreateTextNode(inputString, &inputText)); - HRESULT hr = d->m_toastXml->CreateTextNode(inputString, &inputText); - if (SUCCEEDED(hr)) { - ComPtr inputTextNode; + ComPtr inputTextNode; + ReturnOnErrorHr(inputText.As(&inputTextNode)); - hr = inputText.As(&inputTextNode); - if (SUCCEEDED(hr)) { - ComPtr pAppendedChild; - hr = node->AppendChild(inputTextNode.Get(), &pAppendedChild); - } - } - - return hr; + ComPtr pAppendedChild; + return node->AppendChild(inputTextNode.Get(), &pAppendedChild); } HRESULT SnoreToasts::addAttribute(const std::wstring &name, IXmlNamedNodeMap *attributeMap) { ComPtr srcAttribute; - HRESULT hr = d->m_toastXml->CreateAttribute(StringReferenceWrapper(name).Get(), &srcAttribute); + HRESULT hr = d->m_toastXml->CreateAttribute(StringRef(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; } HRESULT SnoreToasts::addAttribute(const std::wstring &name, IXmlNamedNodeMap *attributeMap, const std::wstring &value) { ComPtr srcAttribute; - HRESULT hr = d->m_toastXml->CreateAttribute(StringReferenceWrapper(name).Get(), &srcAttribute); + ReturnOnErrorHr(d->m_toastXml->CreateAttribute(StringRef(name).Get(), &srcAttribute)); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = srcAttribute.As(&node); - if (SUCCEEDED(hr)) { - ComPtr pNode; - hr = attributeMap->SetNamedItem(node.Get(), &pNode); - hr = setNodeValueString(StringReferenceWrapper(value).Get(), node.Get()); - } - } - return hr; + ComPtr node; + ReturnOnErrorHr(srcAttribute.As(&node)); + + ComPtr pNode; + ReturnOnErrorHr(attributeMap->SetNamedItem(node.Get(), &pNode)); + return setNodeValueString(StringRef(value).Get(), node.Get()); } HRESULT SnoreToasts::createNewActionButton(ComPtr actionsNode, const std::wstring &value) { ComPtr actionElement; - HRESULT hr = - d->m_toastXml->CreateElement(StringReferenceWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) { - ComPtr actionNodeTmp; - hr = actionElement.As(&actionNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr actionNode; - actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode); - ComPtr actionAttributes; - hr = actionNode->get_Attributes(&actionAttributes); - if (SUCCEEDED(hr)) { - hr &= addAttribute(L"content", actionAttributes.Get(), value); - - const auto data = formatAction(SnoreToastActions::Actions::ButtonClicked, - { { L"button", value } }); - hr &= addAttribute(L"arguments", actionAttributes.Get(), data); - hr &= addAttribute(L"activationType", actionAttributes.Get(), L"foreground"); - } - } - } + ReturnOnErrorHr(d->m_toastXml->CreateElement(StringRef(L"action").Get(), &actionElement)); + ComPtr actionNodeTmp; + ReturnOnErrorHr(actionElement.As(&actionNodeTmp)); - return hr; + ComPtr actionNode; + ReturnOnErrorHr(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); + + ComPtr actionAttributes; + ReturnOnErrorHr(actionNode->get_Attributes(&actionAttributes)); + + ReturnOnErrorHr(addAttribute(L"content", actionAttributes.Get(), value)); + + const auto data = + formatAction(SnoreToastActions::Actions::ButtonClicked, { { L"button", value } }); + ReturnOnErrorHr(addAttribute(L"arguments", actionAttributes.Get(), data)); + return addAttribute(L"activationType", actionAttributes.Get(), L"foreground"); } void SnoreToasts::printXML() { ComPtr s; ComPtr ss(d->m_toastXml); ss.As(&s); HSTRING string; s->GetXml(&string); PCWSTR str = WindowsGetStringRawBuffer(string, nullptr); tLog << L"------------------------\n\t\t\t" << str << L"\n\t\t" << L"------------------------"; } std::filesystem::path SnoreToasts::pipeName() const { return d->m_pipeName; } void SnoreToasts::setPipeName(const std::filesystem::path &pipeName) { d->m_pipeName = pipeName; } std::filesystem::path SnoreToasts::application() const { return d->m_application; } void SnoreToasts::setApplication(const std::filesystem::path &application) { d->m_application = application; } std::wstring SnoreToasts::formatAction( const SnoreToastActions::Actions &action, const std::vector> &extraData) const { const auto pipe = d->m_pipeName.wstring(); const auto application = d->m_application.wstring(); std::vector> data = { { L"action", SnoreToastActions::getActionString(action) }, { L"notificationId", std::wstring_view(d->m_id) }, { L"pipe", std::wstring_view(pipe) }, { L"application", std::wstring_view(application) } }; data.insert(data.end(), extraData.cbegin(), extraData.cend()); return Utils::formatData(data); } // Create and display the toast HRESULT SnoreToasts::createToast() { - HRESULT hr = d->m_toastManager->CreateToastNotifierWithId( - StringReferenceWrapper(d->m_appID).Get(), &d->m_notifier); - if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) { - ComPtr factory; - hr = GetActivationFactory( - StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification) - .Get(), - &factory); - if (SUCCEEDED(hr)) { - hr = factory->CreateToastNotification(d->m_toastXml.Get(), &d->m_notification); - if (SUCCEEDED(hr)) { - ComPtr toastV2; - hr = d->m_notification.As(&toastV2); - if (SUCCEEDED(hr)) { - hr = toastV2->put_Tag(StringReferenceWrapper(d->m_id).Get()); - tLog << "Put Tag:" << d->m_id << hr; - hr = toastV2->put_Group(StringReferenceWrapper(L"SnoreToast").Get()); - tLog << "Put Group: SnoreToast" << hr; - } - if (d->m_wait) { - NotificationSetting setting = NotificationSetting_Enabled; - d->m_notifier->get_Setting(&setting); - if (setting == NotificationSetting_Enabled) { - hr = setEventHandler(d->m_notification); - } else { - std::wcerr << L"Notifications are disabled" << std::endl << L"Reason: "; - switch (setting) { - case NotificationSetting_DisabledForApplication: - std::wcerr << L"DisabledForApplication" << std::endl; - break; - case NotificationSetting_DisabledForUser: - std::wcerr << L"DisabledForUser" << std::endl; - break; - case NotificationSetting_DisabledByGroupPolicy: - std::wcerr << L"DisabledByGroupPolicy" << std::endl; - break; - case NotificationSetting_DisabledByManifest: - std::wcerr << L"DisabledByManifest" << std::endl; - break; - case NotificationSetting_Enabled: - // unreachable - break; - } - std::wcerr << L"Please make sure that the app id is set correctly." - << std::endl; - std::wcerr << L"Command Line: " << GetCommandLineW() << std::endl; - hr = S_FALSE; - } - } - if (SUCCEEDED(hr)) { - hr = d->m_notifier->Show(d->m_notification.Get()); - } - } + ReturnOnErrorHr(d->m_toastManager->CreateToastNotifierWithId(StringRef(d->m_appID).Get(), + &d->m_notifier)); + + ComPtr factory; + ReturnOnErrorHr(GetActivationFactory( + StringRef(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory)); + ReturnOnErrorHr(factory->CreateToastNotification(d->m_toastXml.Get(), &d->m_notification)); + + ComPtr toastV2; + if (SUCCEEDED(d->m_notification.As(&toastV2))) { + ReturnOnErrorHr(toastV2->put_Tag(StringRef(d->m_id).Get())); + ReturnOnErrorHr(toastV2->put_Group(StringRef(L"SnoreToast").Get())); + } + + if (d->m_wait) { + NotificationSetting setting = NotificationSetting_Enabled; + d->m_notifier->get_Setting(&setting); + if (setting == NotificationSetting_Enabled) { + ReturnOnErrorHr(setEventHandler(d->m_notification)); + } else { + std::wcerr << L"Notifications are disabled" << std::endl << L"Reason: "; + switch (setting) { + case NotificationSetting_DisabledForApplication: + std::wcerr << L"DisabledForApplication" << std::endl; + break; + case NotificationSetting_DisabledForUser: + std::wcerr << L"DisabledForUser" << std::endl; + break; + case NotificationSetting_DisabledByGroupPolicy: + std::wcerr << L"DisabledByGroupPolicy" << std::endl; + break; + case NotificationSetting_DisabledByManifest: + std::wcerr << L"DisabledByManifest" << std::endl; + break; + case NotificationSetting_Enabled: + // unreachable + break; } + std::wcerr << L"Please make sure that the app id is set correctly." << std::endl; + std::wcerr << L"Command Line: " << GetCommandLineW() << std::endl; + return S_FALSE; } } - return hr; + + return d->m_notifier->Show(d->m_notification.Get()); } std::wstring SnoreToasts::version() { static std::wstring ver = [] { std::wstringstream st; st << SNORETOAST_VERSION_MAJOR << L"." << SNORETOAST_VERSION_MINOR << L"." << SNORETOAST_VERSION_PATCH; return st.str(); }(); return ver; } HRESULT SnoreToasts::backgroundCallback(const std::wstring &appUserModelId, const std::wstring &invokedArgs, const std::wstring &msg) { tLog << "CToastNotificationActivationCallback::Activate: " << appUserModelId << " : " << invokedArgs << " : " << msg; const auto dataMap = Utils::splitData(invokedArgs); const auto action = SnoreToastActions::getAction(dataMap.at(L"action")); std::wstring dataString; if (action == SnoreToastActions::Actions::TextEntered) { std::wstringstream sMsg; sMsg << invokedArgs << L"text=" << msg; dataString = sMsg.str(); } else { dataString = invokedArgs; } const auto pipe = dataMap.find(L"pipe"); if (pipe != dataMap.cend()) { if (!Utils::writePipe(pipe->second, dataString)) { const auto app = dataMap.find(L"application"); if (app != dataMap.cend()) { if (Utils::startProcess(app->second)) { Utils::writePipe(pipe->second, dataString, true); } } } } tLog << dataString; if (!SetEvent(SnoreToastsPrivate::ctoastEvent())) { tLog << "SetEvent failed" << GetLastError(); return S_FALSE; } return S_OK; } void SnoreToasts::waitForCallbackActivation() { Utils::registerActivator(); WaitForSingleObject(SnoreToastsPrivate::ctoastEvent(), INFINITE); Utils::unregisterActivator(); } diff --git a/src/snoretoasts.h b/src/snoretoasts.h index ecb155b..08b325d 100644 --- a/src/snoretoasts.h +++ b/src/snoretoasts.h @@ -1,103 +1,103 @@ /* SnoreToast is capable to invoke Windows 8 toast notifications. Copyright (C) 2013-2019 Hannah von Reth SnoreToast is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SnoreToast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SnoreToast. If not, see . */ #pragma once #include "snoretoastactions.h" #include "libsnoretoast_export.h" #include // Windows Header Files: #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; class LIBSNORETOAST_EXPORT SnoreToasts { public: static std::wstring version(); static void waitForCallbackActivation(); static HRESULT backgroundCallback(const std::wstring &appUserModelId, const std::wstring &invokedArgs, const std::wstring &msg); SnoreToasts(const std::wstring &appID); ~SnoreToasts(); - void displayToast(const std::wstring &title, const std::wstring &body, - const std::filesystem::path &image, bool wait); + HRESULT displayToast(const std::wstring &title, const std::wstring &body, + const std::filesystem::path &image, bool wait); SnoreToastActions::Actions userAction(); bool closeNotification(); void setSound(const std::wstring &soundFile); void setSilent(bool silent); void setId(const std::wstring &id); std::wstring id() const; void setButtons(const std::wstring &buttons); void setTextBoxEnabled(bool textBoxEnabled); std::filesystem::path pipeName() const; void setPipeName(const std::filesystem::path &pipeName); std::filesystem::path application() const; void setApplication(const std::filesystem::path &application); std::wstring formatAction(const SnoreToastActions::Actions &action, const std::vector> &extraData = {}) const; private: HRESULT createToast(); HRESULT setImage(); HRESULT setSound(); HRESULT setTextValues(); HRESULT setButtons(ComPtr root); HRESULT setTextBox(ComPtr root); HRESULT setEventHandler( Microsoft::WRL::ComPtr toast); HRESULT setNodeValueString(const HSTRING &onputString, ABI::Windows::Data::Xml::Dom::IXmlNode *node); HRESULT addAttribute(const std::wstring &name, ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap *attributeMap); HRESULT addAttribute(const std::wstring &name, ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap *attributeMap, const std::wstring &value); HRESULT createNewActionButton(ComPtr actionsNode, const std::wstring &value); void printXML(); friend class SnoreToastsPrivate; SnoreToastsPrivate *d; }; diff --git a/src/stringreferencewrapper.h b/src/stringreferencewrapper.h index adbf44d..1c62f68 100644 --- a/src/stringreferencewrapper.h +++ b/src/stringreferencewrapper.h @@ -1,68 +1,68 @@ #include #include #include -class StringReferenceWrapper +class StringRef { public: // Constructor which takes an existing string buffer and its length as the parameters. // It fills an HSTRING_HEADER struct with the parameter. // Warning: The caller must ensure the lifetime of the buffer outlives this // object as it does not make a copy of the wide string memory. - StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() + StringRef(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } } - StringReferenceWrapper(const std::wstring &stringRef) throw() : m_data(stringRef) + StringRef(const std::wstring &stringRef) throw() : m_data(stringRef) { HRESULT hr = WindowsCreateStringReference( m_data.c_str(), static_cast(m_data.length()), &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } } - ~StringReferenceWrapper() { WindowsDeleteString(_hstring); } + ~StringRef() { WindowsDeleteString(_hstring); } template - StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() + StringRef(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() { UINT32 length = N - 1; HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } } template - StringReferenceWrapper(_In_reads_(_) wchar_t (&stringRef)[_]) throw() + StringRef(_In_reads_(_) wchar_t (&stringRef)[_]) throw() { UINT32 length; HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length); if (FAILED(hr)) { RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); } WindowsCreateStringReference(stringRef, length, &_header, &_hstring); } HSTRING Get() const throw() { return _hstring; } private: HSTRING _hstring; HSTRING_HEADER _header; std::wstring m_data; }; diff --git a/src/utils.h b/src/utils.h index 2047565..1816bca 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,71 +1,90 @@ /* SnoreToast is capable to invoke Windows 8 toast notifications. Copyright (C) 2019 Hannah von Reth SnoreToast is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SnoreToast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SnoreToast. If not, see . */ #pragma once #include #include #include #include -namespace Utils { -bool registerActivator(); -void unregisterActivator(); - -std::unordered_map splitData(const std::wstring_view &data); - -const std::filesystem::path &selfLocate(); - -std::wstring formatData(const std::vector> &data); - -bool writePipe(const std::filesystem::path &pipe, const std::wstring &data, bool wait = false); -bool startProcess(const std::filesystem::path &app); -}; +class ToastLog; class ToastLog { public: ToastLog(); ~ToastLog(); inline ToastLog &log() { return *this; } private: std::wstringstream m_log; template friend ToastLog &operator<<(ToastLog &, const T &); }; -#define tLog ToastLog().log() << __FUNCSIG__ << L"\n\t\t" - template ToastLog &operator<<(ToastLog &log, const T &t) { log.m_log << L" " << t; return log; } template<> inline ToastLog &operator<<(ToastLog &log, const HRESULT &hr) { - if (hr) { + if (FAILED(hr)) { _com_error err(hr); log.m_log << L" Error: " << hr << L" " << err.ErrorMessage(); } return log; } + +#define tLog ToastLog().log() << __FUNCSIG__ << L"\n\t\t" + +#define ReturnOnErrorHr(hr) \ + do { \ + if (!SUCCEEDED(hr)) { \ + tLog << hr; \ + return hr; \ + } \ + } while (false) + +namespace Utils { +bool registerActivator(); +void unregisterActivator(); + +std::unordered_map splitData(const std::wstring_view &data); + +const std::filesystem::path &selfLocate(); + +std::wstring formatData(const std::vector> &data); + +bool writePipe(const std::filesystem::path &pipe, const std::wstring &data, bool wait = false); +bool startProcess(const std::filesystem::path &app); + +inline bool checkResult(const HRESULT &hr) +{ + if (FAILED(hr)) { + tLog << hr; + return false; + } + return true; +} +}; \ No newline at end of file