diff --git a/src/toasteventhandler.cpp b/src/toasteventhandler.cpp index 3a1705f..3ab36f8 100644 --- a/src/toasteventhandler.cpp +++ b/src/toasteventhandler.cpp @@ -1,200 +1,202 @@ /* 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 "utils.h" #include #include #include #include #include using namespace ABI::Windows::UI::Notifications; ToastEventHandler::ToastEventHandler(const std::wstring &id) : m_ref(1), m_userAction(SnoreToasts::Hidden) + , m_id(id) { std::wstringstream eventName; eventName << L"ToastEvent" << id; m_event = CreateEventW(nullptr, true, false, eventName.str().c_str()); } ToastEventHandler::~ToastEventHandler() { CloseHandle(m_event); } HANDLE ToastEventHandler::event() { return m_event; } SnoreToasts::USER_ACTION &ToastEventHandler::userAction() { return m_userAction; } // DesktopToastActivatedEventHandler IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification * /*sender*/, _In_ IInspectable *args) { IToastActivatedEventArgs *buttonReply = nullptr; args->QueryInterface(&buttonReply); if (buttonReply == nullptr) { std::wcerr << L"args is not a IToastActivatedEventArgs" << std::endl; } else { HSTRING args; buttonReply->get_Arguments(&args); PCWSTR _str = WindowsGetStringRawBuffer(args, nullptr); const std::wstring str = _str; const auto data = Utils::splitData(CToastNotificationActivationCallback::data()); const auto action = data.at(L"action"); + assert(data.at(L"notificationId") == m_id); if (action == Actions::Reply) { tLog << L"The user entered a text."; std::wcout << data.at(L"text") << std::endl; m_userAction = SnoreToasts::TextEntered; } else if (action == Actions::Clicked) { tLog << L"The user clicked on the toast."; m_userAction = SnoreToasts::Success; } else { tLog << L"The user clicked on a toast button."; std::wcout << data.at(L"button") << std::endl; m_userAction = SnoreToasts::ButtonPressed; } } SetEvent(m_event); return S_OK; } // DesktopToastDismissedEventHandler IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification * /* sender */, _In_ IToastDismissedEventArgs *e) { ToastDismissalReason tdr; HRESULT hr = e->get_Reason(&tdr); if (SUCCEEDED(hr)) { switch (tdr) { case ToastDismissalReason_ApplicationHidden: tLog << L"The application hid the toast using ToastNotifier.hide()"; m_userAction = SnoreToasts::Hidden; break; case ToastDismissalReason_UserCanceled: tLog << L"The user dismissed this toast"; m_userAction = SnoreToasts::Dismissed; break; case ToastDismissalReason_TimedOut: tLog << L"The toast has timed out"; m_userAction = SnoreToasts::TimedOut; break; } } SetEvent(m_event); return S_OK; } // DesktopToastFailedEventHandler IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification * /* sender */, _In_ IToastFailedEventArgs * /* e */) { std::wcerr << L"The toast encountered an error." << std::endl; std::wcerr << L"Please make sure that the app id is set correctly." << std::endl; std::wcerr << L"Command Line: " << GetCommandLineW() << std::endl; m_userAction = SnoreToasts::Failed; SetEvent(m_event); return S_OK; } CToastNotificationActivationCallback::CToastNotificationActivationCallback() { } HANDLE CToastNotificationActivationCallback::m_event = INVALID_HANDLE_VALUE; std::wstring CToastNotificationActivationCallback::m_data = {}; HRESULT CToastNotificationActivationCallback::Activate(LPCWSTR appUserModelId, LPCWSTR invokedArgs, const NOTIFICATION_USER_INPUT_DATA* data, ULONG count) { if (invokedArgs == nullptr) { return S_OK; } tLog << "CToastNotificationActivationCallback::Activate: " << appUserModelId << " : " << invokedArgs << " : " << data; const auto dataMap = Utils::splitData(invokedArgs); if (dataMap.at(L"action") == Actions::Reply) { assert(count); std::wstringstream sMsg; sMsg << invokedArgs << L"text="; for (ULONG i=0; isecond, m_data); } tLog << m_data; if (m_event != INVALID_HANDLE_VALUE) { SetEvent(m_event); } return S_OK; } std::wstring CToastNotificationActivationCallback::waitForActivation() { if (m_event == INVALID_HANDLE_VALUE) { std::wstringstream eventName; eventName << L"ToastActivationEvent" << GetCurrentProcessId(); m_event = CreateEventW(nullptr, true, false, eventName.str().c_str()); } else { ResetEvent(m_event); } WaitForSingleObject(m_event, INFINITE); return m_data; } std::wstring CToastNotificationActivationCallback::data() { return m_data; } diff --git a/src/toasteventhandler.h b/src/toasteventhandler.h index 9e8f58b..c5bcd6c 100644 --- a/src/toasteventhandler.h +++ b/src/toasteventhandler.h @@ -1,132 +1,133 @@ /* 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 "snoretoasts.h" #include "wrl.h" #define TOAST_UUID "{383803B6-AFDA-4220-BFC3-0DBF810106BF}" namespace Actions { static const std::wstring Reply = L"reply"; static const std::wstring Clicked = L"clicked"; static const std::wstring Button = L"button"; } typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastActivatedEventHandler; typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastDismissedEventHandler; typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastFailedEventHandler; //Define INotificationActivationCallback for older versions of the Windows SDK #include typedef struct NOTIFICATION_USER_INPUT_DATA { LPCWSTR Key; LPCWSTR Value; } NOTIFICATION_USER_INPUT_DATA; MIDL_INTERFACE("53E31837-6600-4A81-9395-75CFFE746F94") INotificationActivationCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Activate(__RPC__in_string LPCWSTR appUserModelId, __RPC__in_opt_string LPCWSTR invokedArgs, __RPC__in_ecount_full_opt(count) const NOTIFICATION_USER_INPUT_DATA *data, ULONG count) = 0; }; //The COM server which implements the callback notifcation from Action Center class DECLSPEC_UUID(TOAST_UUID) CToastNotificationActivationCallback : public Microsoft::WRL::RuntimeClass, INotificationActivationCallback> { public: static std::wstring waitForActivation(); static std::wstring data(); CToastNotificationActivationCallback(); virtual HRESULT STDMETHODCALLTYPE Activate(__RPC__in_string LPCWSTR appUserModelId, __RPC__in_opt_string LPCWSTR invokedArgs, __RPC__in_ecount_full_opt(count) const NOTIFICATION_USER_INPUT_DATA* data, ULONG count) override; private: static HANDLE m_event; static std::wstring m_data; }; CoCreatableClass(CToastNotificationActivationCallback); class ToastEventHandler : public Microsoft::WRL::Implements { public: ToastEventHandler::ToastEventHandler(const std::wstring &id); ~ToastEventHandler(); HANDLE event(); SnoreToasts::USER_ACTION &userAction(); // DesktopToastActivatedEventHandler IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ IInspectable *args); // DesktopToastDismissedEventHandler IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastDismissedEventArgs *e); // DesktopToastFailedEventHandler IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastFailedEventArgs *e); // IUnknown IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); } IFACEMETHODIMP_(ULONG) Release() { ULONG l = InterlockedDecrement(&m_ref); if (l == 0) { delete this; } return l; } IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { if (IsEqualIID(riid, IID_IUnknown)) { *ppv = static_cast(static_cast(this)); } else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) { *ppv = static_cast(this); } else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) { *ppv = static_cast(this); } else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler))) { *ppv = static_cast(this); } else { *ppv = nullptr; } if (*ppv) { reinterpret_cast(*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } private: ULONG m_ref; SnoreToasts::USER_ACTION m_userAction; HANDLE m_event; + std::wstring m_id; };