diff --git a/src/linkhelper.cpp b/src/linkhelper.cpp index 0b8d438..483e2a0 100644 --- a/src/linkhelper.cpp +++ b/src/linkhelper.cpp @@ -1,136 +1,136 @@ /* 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 "linkhelper.h" #include "toasteventhandler.h" #include "utils.h" #include #include #include #include // compat with older sdk #ifndef INIT_PKEY_AppUserModel_ToastActivatorCLSID EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY PKEY_AppUserModel_ToastActivatorCLSID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 26 }; #define INIT_PKEY_AppUserModel_ToastActivatorCLSID \ { \ { 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 }, 26 \ } #endif //#ifndef INIT_PKEY_AppUserModel_ToastActivatorCLSID HRESULT LinkHelper::tryCreateShortcut(const std::filesystem::path &shortcutPath, const std::filesystem::path &exePath, const std::wstring &appID, const std::wstring &callbackUUID) { std::filesystem::path path = shortcutPath; if (path.is_relative()) { path = startmenuPath() / path; } - // make sure the extension is set + // make sure the extension is set path.replace_extension(L".lnk"); if (std::filesystem::exists(path)) { tLog << L"Path: " << path << L" already exists, skip creation of shortcut"; return S_OK; } if (!std::filesystem::exists(path.parent_path()) && !std::filesystem::create_directories(path.parent_path())) { tLog << L"Failed to create dir: " << path.parent_path(); return S_FALSE; } return installShortcut(path, exePath, appID, callbackUUID); } HRESULT LinkHelper::tryCreateShortcut(const std::filesystem::path &shortcutPath, const std::wstring &appID, const std::wstring &callbackUUID) { return tryCreateShortcut(shortcutPath, Utils::selfLocate(), appID, callbackUUID); } // Install the shortcut HRESULT LinkHelper::installShortcut(const std::filesystem::path &shortcutPath, const std::filesystem::path &exePath, const std::wstring &appID, const std::wstring &callbackUUID) { std::wcout << L"Installing shortcut: " << shortcutPath << L" " << exePath << L" " << appID << std::endl; tLog << L"Installing shortcut: " << shortcutPath << L" " << exePath << L" " << appID << L" " << callbackUUID; if (!callbackUUID.empty()) { /** * Add CToastNotificationActivationCallback to registry * Required to use the CToastNotificationActivationCallback for buttons and textbox * interactions. windows.ui.notifications does not support user interaction from cpp */ const std::wstring locPath = Utils::selfLocate().wstring(); const std::wstring url = [&callbackUUID] { std::wstringstream url; url << L"SOFTWARE\\Classes\\CLSID\\" << callbackUUID << L"\\LocalServer32"; return url.str(); }(); tLog << url; - ReturnOnErrorHr(HRESULT_FROM_WIN32( + ST_RETURN_ON_ERROR(HRESULT_FROM_WIN32( ::RegSetKeyValueW(HKEY_CURRENT_USER, url.c_str(), nullptr, REG_SZ, locPath.c_str(), static_cast(locPath.size() * sizeof(wchar_t))))); } ComPtr shellLink; - ReturnOnErrorHr(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&shellLink))); - ReturnOnErrorHr(shellLink->SetPath(exePath.c_str())); - ReturnOnErrorHr(shellLink->SetArguments(L"")); + ST_RETURN_ON_ERROR(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&shellLink))); + ST_RETURN_ON_ERROR(shellLink->SetPath(exePath.c_str())); + ST_RETURN_ON_ERROR(shellLink->SetArguments(L"")); ComPtr propertyStore; - ReturnOnErrorHr(shellLink.As(&propertyStore)); + ST_RETURN_ON_ERROR(shellLink.As(&propertyStore)); PROPVARIANT appIdPropVar; - ReturnOnErrorHr(InitPropVariantFromString(appID.c_str(), &appIdPropVar)); - ReturnOnErrorHr(propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar)); + ST_RETURN_ON_ERROR(InitPropVariantFromString(appID.c_str(), &appIdPropVar)); + ST_RETURN_ON_ERROR(propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar)); PropVariantClear(&appIdPropVar); if (!callbackUUID.empty()) { GUID guid; - ReturnOnErrorHr(CLSIDFromString(callbackUUID.c_str(), &guid)); + ST_RETURN_ON_ERROR(CLSIDFromString(callbackUUID.c_str(), &guid)); tLog << guid.Data1; PROPVARIANT toastActivatorPropVar = {}; toastActivatorPropVar.vt = VT_CLSID; toastActivatorPropVar.puuid = &guid; - ReturnOnErrorHr(propertyStore->SetValue(PKEY_AppUserModel_ToastActivatorCLSID, - toastActivatorPropVar)); + ST_RETURN_ON_ERROR(propertyStore->SetValue(PKEY_AppUserModel_ToastActivatorCLSID, + toastActivatorPropVar)); } - ReturnOnErrorHr(propertyStore->Commit()); + ST_RETURN_ON_ERROR(propertyStore->Commit()); ComPtr persistFile; - ReturnOnErrorHr(shellLink.As(&persistFile)); + ST_RETURN_ON_ERROR(shellLink.As(&persistFile)); return persistFile->Save(shortcutPath.c_str(), true); } std::filesystem::path LinkHelper::startmenuPath() { wchar_t buffer[MAX_PATH]; std::wstringstream path; if (GetEnvironmentVariable(L"APPDATA", buffer, MAX_PATH) > 0) { path << buffer << L"\\Microsoft\\Windows\\Start Menu\\Programs\\"; } return path.str(); } diff --git a/src/snoretoasts.cpp b/src/snoretoasts.cpp index 38f21f6..3d38d9a 100644 --- a/src/snoretoasts.cpp +++ b/src/snoretoasts.cpp @@ -1,598 +1,602 @@ /* 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 #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; using namespace Wrappers; 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( HStringReference(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_textbox = false; SnoreToastActions::Actions m_action = SnoreToastActions::Actions::Clicked; ComPtr m_toastXml; 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() { ComPtr toastStatics2; - if (Utils::checkResult(m_toastManager.As(&toastStatics2))) { + if (ST_CHECK_RESULT(m_toastManager.As(&toastStatics2))) { ComPtr nativeHistory; - Utils::checkResult(toastStatics2->get_History(&nativeHistory)); + ST_CHECK_RESULT(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; } HRESULT SnoreToasts::displayToast(const std::wstring &title, const std::wstring &body, const std::filesystem::path &image) { // asume that we fail d->m_action = SnoreToastActions::Actions::Error; d->m_title = title; d->m_body = body; d->m_image = image; if (!d->m_image.empty()) { - ReturnOnErrorHr(d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastImageAndText02, - &d->m_toastXml)); + ST_RETURN_ON_ERROR(d->m_toastManager->GetTemplateContent( + ToastTemplateType_ToastImageAndText02, &d->m_toastXml)); } else { - ReturnOnErrorHr(d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastText02, - &d->m_toastXml)); + ST_RETURN_ON_ERROR(d->m_toastManager->GetTemplateContent(ToastTemplateType_ToastText02, + &d->m_toastXml)); } ComPtr rootList; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->GetElementsByTagName(HStringReference(L"toast").Get(), &rootList)); ComPtr root; - ReturnOnErrorHr(rootList->Item(0, &root)); + ST_RETURN_ON_ERROR(rootList->Item(0, &root)); ComPtr rootAttributes; - ReturnOnErrorHr(root->get_Attributes(&rootAttributes)); + ST_RETURN_ON_ERROR(root->get_Attributes(&rootAttributes)); const auto data = formatAction(SnoreToastActions::Actions::Clicked); - ReturnOnErrorHr(addAttribute(L"launch", rootAttributes.Get(), data)); + ST_RETURN_ON_ERROR(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(HStringReference(L"audio").Get(), &audioElement)); + ST_RETURN_ON_ERROR( + d->m_toastXml->CreateElement(HStringReference(L"audio").Get(), &audioElement)); ComPtr audioNodeTmp; - ReturnOnErrorHr(audioElement.As(&audioNodeTmp)); + ST_RETURN_ON_ERROR(audioElement.As(&audioNodeTmp)); ComPtr audioNode; - ReturnOnErrorHr(root->AppendChild(audioNodeTmp.Get(), &audioNode)); + ST_RETURN_ON_ERROR(root->AppendChild(audioNodeTmp.Get(), &audioNode)); ComPtr attributes; - ReturnOnErrorHr(audioNode->get_Attributes(&attributes)); - ReturnOnErrorHr(addAttribute(L"src", attributes.Get())); - ReturnOnErrorHr(addAttribute(L"silent", attributes.Get())); + ST_RETURN_ON_ERROR(audioNode->get_Attributes(&attributes)); + ST_RETURN_ON_ERROR(addAttribute(L"src", attributes.Get())); + ST_RETURN_ON_ERROR(addAttribute(L"silent", attributes.Get())); // printXML(); if (!d->m_image.empty()) { - ReturnOnErrorHr(setImage()); + ST_RETURN_ON_ERROR(setImage()); } - ReturnOnErrorHr(setSound()); + ST_RETURN_ON_ERROR(setSound()); - ReturnOnErrorHr(setTextValues()); + ST_RETURN_ON_ERROR(setTextValues()); printXML(); - ReturnOnErrorHr(createToast()); + ST_RETURN_ON_ERROR(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(); // the initial value is SnoreToastActions::Actions::Hidden so if no action happend when we // end up here, a hide was requested 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; } if (auto history = d->getHistory()) { - if (Utils::checkResult(history->RemoveGroupedTagWithId( + if (ST_CHECK_RESULT(history->RemoveGroupedTagWithId( HStringReference(d->m_id.c_str()).Get(), HStringReference(L"SnoreToast").Get(), HStringReference(d->m_appID.c_str()).Get()))) { return true; } } 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() { ComPtr nodeList; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->GetElementsByTagName(HStringReference(L"image").Get(), &nodeList)); ComPtr imageNode; - ReturnOnErrorHr(nodeList->Item(0, &imageNode)); + ST_RETURN_ON_ERROR(nodeList->Item(0, &imageNode)); ComPtr attributes; - ReturnOnErrorHr(imageNode->get_Attributes(&attributes)); + ST_RETURN_ON_ERROR(imageNode->get_Attributes(&attributes)); ComPtr srcAttribute; - ReturnOnErrorHr(attributes->GetNamedItem(HStringReference(L"src").Get(), &srcAttribute)); + ST_RETURN_ON_ERROR(attributes->GetNamedItem(HStringReference(L"src").Get(), &srcAttribute)); return setNodeValueString(HStringReference(d->m_image.wstring().c_str()).Get(), srcAttribute.Get()); } HRESULT SnoreToasts::setSound() { ComPtr nodeList; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->GetElementsByTagName(HStringReference(L"audio").Get(), &nodeList)); ComPtr audioNode; - ReturnOnErrorHr(nodeList->Item(0, &audioNode)); + ST_RETURN_ON_ERROR(nodeList->Item(0, &audioNode)); ComPtr attributes; - ReturnOnErrorHr(audioNode->get_Attributes(&attributes)); + ST_RETURN_ON_ERROR(audioNode->get_Attributes(&attributes)); ComPtr srcAttribute; - ReturnOnErrorHr(attributes->GetNamedItem(HStringReference(L"src").Get(), &srcAttribute)); + ST_RETURN_ON_ERROR(attributes->GetNamedItem(HStringReference(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; } - ReturnOnErrorHr(setNodeValueString(HStringReference(sound.c_str()).Get(), srcAttribute.Get())); - ReturnOnErrorHr(attributes->GetNamedItem(HStringReference(L"silent").Get(), &srcAttribute)); + ST_RETURN_ON_ERROR( + setNodeValueString(HStringReference(sound.c_str()).Get(), srcAttribute.Get())); + ST_RETURN_ON_ERROR(attributes->GetNamedItem(HStringReference(L"silent").Get(), &srcAttribute)); return setNodeValueString(HStringReference(d->m_silent ? L"true" : L"false").Get(), srcAttribute.Get()); } // Set the values of each of the text nodes HRESULT SnoreToasts::setTextValues() { ComPtr nodeList; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->GetElementsByTagName(HStringReference(L"text").Get(), &nodeList)); // create the title ComPtr textNode; - ReturnOnErrorHr(nodeList->Item(0, &textNode)); - ReturnOnErrorHr(setNodeValueString(HStringReference(d->m_title.c_str()).Get(), textNode.Get())); - ReturnOnErrorHr(nodeList->Item(1, &textNode)); + ST_RETURN_ON_ERROR(nodeList->Item(0, &textNode)); + ST_RETURN_ON_ERROR( + setNodeValueString(HStringReference(d->m_title.c_str()).Get(), textNode.Get())); + ST_RETURN_ON_ERROR(nodeList->Item(1, &textNode)); return setNodeValueString(HStringReference(d->m_body.c_str()).Get(), textNode.Get()); } HRESULT SnoreToasts::setButtons(ComPtr root) { ComPtr actionsElement; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->CreateElement(HStringReference(L"actions").Get(), &actionsElement)); ComPtr actionsNodeTmp; - ReturnOnErrorHr(actionsElement.As(&actionsNodeTmp)); + ST_RETURN_ON_ERROR(actionsElement.As(&actionsNodeTmp)); ComPtr actionsNode; - ReturnOnErrorHr(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); + ST_RETURN_ON_ERROR(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); std::wstring buttonText; std::wstringstream wss(d->m_buttons); while (std::getline(wss, buttonText, L';')) { - ReturnOnErrorHr(createNewActionButton(actionsNode, buttonText)); + ST_RETURN_ON_ERROR(createNewActionButton(actionsNode, buttonText)); } return S_OK; } HRESULT SnoreToasts::setTextBox(ComPtr root) { ComPtr actionsElement; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->CreateElement(HStringReference(L"actions").Get(), &actionsElement)); ComPtr actionsNodeTmp; - ReturnOnErrorHr(actionsElement.As(&actionsNodeTmp)); + ST_RETURN_ON_ERROR(actionsElement.As(&actionsNodeTmp)); ComPtr actionsNode; - ReturnOnErrorHr(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); + ST_RETURN_ON_ERROR(root->AppendChild(actionsNodeTmp.Get(), &actionsNode)); ComPtr inputElement; - ReturnOnErrorHr(d->m_toastXml->CreateElement(HStringReference(L"input").Get(), &inputElement)); + ST_RETURN_ON_ERROR( + d->m_toastXml->CreateElement(HStringReference(L"input").Get(), &inputElement)); ComPtr inputNodeTmp; - ReturnOnErrorHr(inputElement.As(&inputNodeTmp)); + ST_RETURN_ON_ERROR(inputElement.As(&inputNodeTmp)); ComPtr inputNode; - ReturnOnErrorHr(actionsNode->AppendChild(inputNodeTmp.Get(), &inputNode)); + ST_RETURN_ON_ERROR(actionsNode->AppendChild(inputNodeTmp.Get(), &inputNode)); ComPtr inputAttributes; - ReturnOnErrorHr(inputNode->get_Attributes(&inputAttributes)); + ST_RETURN_ON_ERROR(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")); + ST_RETURN_ON_ERROR(addAttribute(L"id", inputAttributes.Get(), L"textBox")); + ST_RETURN_ON_ERROR(addAttribute(L"type", inputAttributes.Get(), L"text")); + ST_RETURN_ON_ERROR(addAttribute(L"placeHolderContent", inputAttributes.Get(), L"Type a reply")); ComPtr actionElement; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->CreateElement(HStringReference(L"action").Get(), &actionElement)); ComPtr actionNodeTmp; - ReturnOnErrorHr(actionElement.As(&actionNodeTmp)); + ST_RETURN_ON_ERROR(actionElement.As(&actionNodeTmp)); ComPtr actionNode; - ReturnOnErrorHr(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); + ST_RETURN_ON_ERROR(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); ComPtr actionAttributes; - ReturnOnErrorHr(actionNode->get_Attributes(&actionAttributes)); + ST_RETURN_ON_ERROR(actionNode->get_Attributes(&actionAttributes)); - ReturnOnErrorHr(addAttribute(L"content", actionAttributes.Get(), L"Send")); + ST_RETURN_ON_ERROR(addAttribute(L"content", actionAttributes.Get(), L"Send")); const auto data = formatAction(SnoreToastActions::Actions::ButtonClicked); - ReturnOnErrorHr(addAttribute(L"arguments", actionAttributes.Get(), data)); + ST_RETURN_ON_ERROR(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)); - ReturnOnErrorHr(toast->add_Activated(eventHandler.Get(), &activatedToken)); - ReturnOnErrorHr(toast->add_Dismissed(eventHandler.Get(), &dismissedToken)); - ReturnOnErrorHr(toast->add_Failed(eventHandler.Get(), &failedToken)); + ST_RETURN_ON_ERROR(toast->add_Activated(eventHandler.Get(), &activatedToken)); + ST_RETURN_ON_ERROR(toast->add_Dismissed(eventHandler.Get(), &dismissedToken)); + ST_RETURN_ON_ERROR(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)); + ST_RETURN_ON_ERROR(d->m_toastXml->CreateTextNode(inputString, &inputText)); ComPtr inputTextNode; - ReturnOnErrorHr(inputText.As(&inputTextNode)); + ST_RETURN_ON_ERROR(inputText.As(&inputTextNode)); 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(HStringReference(name.c_str()).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; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->CreateAttribute(HStringReference(name.c_str()).Get(), &srcAttribute)); ComPtr node; - ReturnOnErrorHr(srcAttribute.As(&node)); + ST_RETURN_ON_ERROR(srcAttribute.As(&node)); ComPtr pNode; - ReturnOnErrorHr(attributeMap->SetNamedItem(node.Get(), &pNode)); + ST_RETURN_ON_ERROR(attributeMap->SetNamedItem(node.Get(), &pNode)); return setNodeValueString(HStringReference(value.c_str()).Get(), node.Get()); } HRESULT SnoreToasts::createNewActionButton(ComPtr actionsNode, const std::wstring &value) { ComPtr actionElement; - ReturnOnErrorHr( + ST_RETURN_ON_ERROR( d->m_toastXml->CreateElement(HStringReference(L"action").Get(), &actionElement)); ComPtr actionNodeTmp; - ReturnOnErrorHr(actionElement.As(&actionNodeTmp)); + ST_RETURN_ON_ERROR(actionElement.As(&actionNodeTmp)); ComPtr actionNode; - ReturnOnErrorHr(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); + ST_RETURN_ON_ERROR(actionsNode->AppendChild(actionNodeTmp.Get(), &actionNode)); ComPtr actionAttributes; - ReturnOnErrorHr(actionNode->get_Attributes(&actionAttributes)); + ST_RETURN_ON_ERROR(actionNode->get_Attributes(&actionAttributes)); - ReturnOnErrorHr(addAttribute(L"content", actionAttributes.Get(), value)); + ST_RETURN_ON_ERROR(addAttribute(L"content", actionAttributes.Get(), value)); const auto data = formatAction(SnoreToastActions::Actions::ButtonClicked, { { L"button", value } }); - ReturnOnErrorHr(addAttribute(L"arguments", actionAttributes.Get(), data)); + ST_RETURN_ON_ERROR(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() { - ReturnOnErrorHr(d->m_toastManager->CreateToastNotifierWithId( + ST_RETURN_ON_ERROR(d->m_toastManager->CreateToastNotifierWithId( HStringReference(d->m_appID.c_str()).Get(), &d->m_notifier)); ComPtr factory; - ReturnOnErrorHr(GetActivationFactory( + ST_RETURN_ON_ERROR(GetActivationFactory( HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory)); - ReturnOnErrorHr(factory->CreateToastNotification(d->m_toastXml.Get(), &d->m_notification)); + ST_RETURN_ON_ERROR(factory->CreateToastNotification(d->m_toastXml.Get(), &d->m_notification)); ComPtr toastV2; if (SUCCEEDED(d->m_notification.As(&toastV2))) { - ReturnOnErrorHr(toastV2->put_Tag(HStringReference(d->m_id.c_str()).Get())); - ReturnOnErrorHr(toastV2->put_Group(HStringReference(L"SnoreToast").Get())); + ST_RETURN_ON_ERROR(toastV2->put_Tag(HStringReference(d->m_id.c_str()).Get())); + ST_RETURN_ON_ERROR(toastV2->put_Group(HStringReference(L"SnoreToast").Get())); } std::wstring error; NotificationSetting setting = NotificationSetting_Enabled; - if (FAILED(d->m_notifier->get_Setting(&setting))) { + if (!ST_CHECK_RESULT(d->m_notifier->get_Setting(&setting))) { tLog << "Failed to retreive NotificationSettings ensure your appId is registered"; } switch (setting) { case NotificationSetting_Enabled: - ReturnOnErrorHr(setEventHandler(d->m_notification)); + ST_RETURN_ON_ERROR(setEventHandler(d->m_notification)); return d->m_notifier->Show(d->m_notification.Get()); case NotificationSetting_DisabledForApplication: error = L"DisabledForApplication"; break; case NotificationSetting_DisabledForUser: error = L"DisabledForUser"; break; case NotificationSetting_DisabledByGroupPolicy: error = L"DisabledByGroupPolicy"; break; case NotificationSetting_DisabledByManifest: error = L"DisabledByManifest"; break; } std::wstringstream err; err << L"Notifications are disabled\n" << L"Reason: " << error << L"Please make sure that the app id is set correctly.\n" << L"Command Line: " << GetCommandLineW(); tLog << err.str(); std::wcerr << err.str() << std::endl; return S_FALSE; } 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/utils.h b/src/utils.h index a4556ed..e7630e9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,91 +1,92 @@ /* 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 class ToastLog; class ToastLog { public: ToastLog(); ~ToastLog(); inline ToastLog &log() { return *this; } private: std::wstringstream m_log; template friend ToastLog &operator<<(ToastLog &, const 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 (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 { \ - HRESULT _tmp = hr; \ - if (FAILED(_tmp)) { \ - tLog << __LINE__ << #hr << _tmp; \ - return _tmp; \ - } \ - } 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) +inline bool checkResult(const char *file, const long line, const char *func, const HRESULT &hr) { if (FAILED(hr)) { - tLog << hr; + tLog << file << line << func << L":\n\t\t\t" << hr; return false; } return true; } }; + +#define ST_CHECK_RESULT(hr) Utils::checkResult(__FILE__, __LINE__, __FUNCSIG__, hr) + +#define ST_RETURN_ON_ERROR(hr) \ + do { \ + HRESULT _tmp = hr; \ + if (!ST_CHECK_RESULT(_tmp)) { \ + return _tmp; \ + } \ + } while (false)