mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-11 17:37:22 +10:00
Update: Add library for Desktop Toast notifications
Based on sample code in: https://github.com/WindowsNotifications/desktop-toasts More info: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop-cpp-wrl
This commit is contained in:
@@ -40,6 +40,9 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ClassicIEDLL", "ClassicIE\ClassicIEDLL\ClassicIEDLL.vcxproj", "{BC0E6E7C-08C1-4F12-A754-4608E5A22FA8}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Update", "Update\Update.vcxproj", "{171B46B0-6083-4D9E-BD33-946EA3BD76FA}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1} = {D94BD2A6-1872-4F01-B911-F406603AA2E1}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win7Aero7", "Skins\Win7Aero7\Win7Aero7.vcxproj", "{A2CCDE9F-17CE-461E-8BD9-00261B8855A6}"
|
||||
EndProject
|
||||
@@ -63,6 +66,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Metro", "Skins\Metro\Metro.
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Metallic7", "Skins\Metallic7\Metallic7.vcxproj", "{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DesktopToasts", "Update\DesktopToasts\DesktopToasts.vcxproj", "{D94BD2A6-1872-4F01-B911-F406603AA2E1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@@ -374,6 +379,15 @@ Global
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|Win32.ActiveCfg = Resource|Win32
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|Win32.Build.0 = Resource|Win32
|
||||
{CA5BFC96-428D-42F5-9F7D-CDDE048A357C}.Setup|x64.ActiveCfg = Resource|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|Win32.Build.0 = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Release|x64.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|Win32.ActiveCfg = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|Win32.Build.0 = Release|Win32
|
||||
{D94BD2A6-1872-4F01-B911-F406603AA2E1}.Setup|x64.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
291
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.cpp
Normal file
291
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE 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 CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
#include "DesktopNotificationManagerCompat.h"
|
||||
#include <appmodel.h>
|
||||
#include <wrl\wrappers\corewrappers.h>
|
||||
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT _hrTemp = hr; if (FAILED(_hrTemp)) { return _hrTemp; } } while (false)
|
||||
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
namespace DesktopNotificationManagerCompat
|
||||
{
|
||||
HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[]);
|
||||
HRESULT EnsureRegistered();
|
||||
bool IsRunningAsUwp();
|
||||
|
||||
bool s_registeredAumidAndComServer = false;
|
||||
std::wstring s_aumid;
|
||||
bool s_registeredActivator = false;
|
||||
bool s_hasCheckedIsRunningAsUwp = false;
|
||||
bool s_isRunningAsUwp = false;
|
||||
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid)
|
||||
{
|
||||
/*
|
||||
// If running as Desktop Bridge
|
||||
if (IsRunningAsUwp())
|
||||
{
|
||||
// Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
|
||||
// Desktop Bridge apps are registered with platform through their manifest.
|
||||
// Their LocalServer32 key is also registered through their manifest.
|
||||
s_aumid = L"";
|
||||
s_registeredAumidAndComServer = true;
|
||||
return S_OK;
|
||||
}
|
||||
*/
|
||||
// Copy the aumid
|
||||
s_aumid = std::wstring(aumid);
|
||||
/*
|
||||
// Get the EXE path
|
||||
wchar_t exePath[MAX_PATH];
|
||||
DWORD charWritten = ::GetModuleFileName(nullptr, exePath, ARRAYSIZE(exePath));
|
||||
RETURN_IF_FAILED(charWritten > 0 ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||
|
||||
// Register the COM server
|
||||
RETURN_IF_FAILED(RegisterComServer(clsid, exePath));
|
||||
*/
|
||||
s_registeredAumidAndComServer = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegisterActivator()
|
||||
{
|
||||
// Module<OutOfProc> needs a callback registered before it can be used.
|
||||
// Since we don't care about when it shuts down, we'll pass an empty lambda here.
|
||||
Module<OutOfProc>::Create([] {});
|
||||
|
||||
// If a local server process only hosts the COM object then COM expects
|
||||
// the COM server host to shutdown when the references drop to zero.
|
||||
// Since the user might still be using the program after activating the notification,
|
||||
// we don't want to shutdown immediately. Incrementing the object count tells COM that
|
||||
// we aren't done yet.
|
||||
Module<OutOfProc>::GetModule().IncrementObjectCount();
|
||||
|
||||
RETURN_IF_FAILED(Module<OutOfProc>::GetModule().RegisterObjects());
|
||||
|
||||
s_registeredActivator = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[])
|
||||
{
|
||||
// Turn the GUID into a string
|
||||
OLECHAR* clsidOlechar;
|
||||
StringFromCLSID(clsid, &clsidOlechar);
|
||||
std::wstring clsidStr(clsidOlechar);
|
||||
::CoTaskMemFree(clsidOlechar);
|
||||
|
||||
// Create the subkey
|
||||
// Something like SOFTWARE\Classes\CLSID\{23A5B06E-20BB-4E7E-A0AC-6982ED6A6041}\LocalServer32
|
||||
std::wstring subKey = LR"(SOFTWARE\Classes\CLSID\)" + clsidStr + LR"(\LocalServer32)";
|
||||
|
||||
// Include -ToastActivated launch args on the exe
|
||||
std::wstring exePathStr(exePath);
|
||||
exePathStr = L"\"" + exePathStr + L"\" " + TOAST_ACTIVATED_LAUNCH_ARG;
|
||||
|
||||
// We don't need to worry about overflow here as ::GetModuleFileName won't
|
||||
// return anything bigger than the max file system path (much fewer than max of DWORD).
|
||||
DWORD dataSize = static_cast<DWORD>((exePathStr.length() + 1) * sizeof(WCHAR));
|
||||
|
||||
// Register the EXE for the COM server
|
||||
return HRESULT_FROM_WIN32(::RegSetKeyValue(
|
||||
HKEY_CURRENT_USER,
|
||||
subKey.c_str(),
|
||||
nullptr,
|
||||
REG_SZ,
|
||||
reinterpret_cast<const BYTE*>(exePathStr.c_str()),
|
||||
dataSize));
|
||||
}
|
||||
|
||||
HRESULT CreateToastNotifier(IToastNotifier **notifier)
|
||||
{
|
||||
RETURN_IF_FAILED(EnsureRegistered());
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics> toastStatics;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(),
|
||||
&toastStatics));
|
||||
|
||||
if (s_aumid.empty())
|
||||
{
|
||||
return toastStatics->CreateToastNotifier(notifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toastStatics->CreateToastNotifierWithId(HStringReference(s_aumid.c_str()).Get(), notifier);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CreateXmlDocumentFromString(const wchar_t *xmlString, IXmlDocument **doc)
|
||||
{
|
||||
ComPtr<IXmlDocument> answer;
|
||||
RETURN_IF_FAILED(Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(), &answer));
|
||||
|
||||
ComPtr<IXmlDocumentIO> docIO;
|
||||
RETURN_IF_FAILED(answer.As(&docIO));
|
||||
|
||||
// Load the XML string
|
||||
RETURN_IF_FAILED(docIO->LoadXml(HStringReference(xmlString).Get()));
|
||||
|
||||
return answer.CopyTo(doc);
|
||||
}
|
||||
|
||||
HRESULT CreateToastNotification(IXmlDocument *content, IToastNotification **notification)
|
||||
{
|
||||
ComPtr<IToastNotificationFactory> factory;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(),
|
||||
&factory));
|
||||
|
||||
return factory->CreateToastNotification(content, notification);
|
||||
}
|
||||
|
||||
HRESULT get_History(std::unique_ptr<DesktopNotificationHistoryCompat>* history)
|
||||
{
|
||||
RETURN_IF_FAILED(EnsureRegistered());
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics> toastStatics;
|
||||
RETURN_IF_FAILED(Windows::Foundation::GetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(),
|
||||
&toastStatics));
|
||||
|
||||
ComPtr<IToastNotificationManagerStatics2> toastStatics2;
|
||||
RETURN_IF_FAILED(toastStatics.As(&toastStatics2));
|
||||
|
||||
ComPtr<IToastNotificationHistory> nativeHistory;
|
||||
RETURN_IF_FAILED(toastStatics2->get_History(&nativeHistory));
|
||||
|
||||
*history = std::unique_ptr<DesktopNotificationHistoryCompat>(new DesktopNotificationHistoryCompat(s_aumid.c_str(), nativeHistory));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool CanUseHttpImages()
|
||||
{
|
||||
return IsRunningAsUwp();
|
||||
}
|
||||
|
||||
HRESULT EnsureRegistered()
|
||||
{
|
||||
// If not registered AUMID yet
|
||||
if (!s_registeredAumidAndComServer)
|
||||
{
|
||||
// Check if Desktop Bridge
|
||||
if (IsRunningAsUwp())
|
||||
{
|
||||
// Implicitly registered, all good!
|
||||
s_registeredAumidAndComServer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, incorrect usage, must call RegisterAumidAndComServer first
|
||||
return E_ILLEGAL_METHOD_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
// If not registered activator yet
|
||||
if (!s_registeredActivator)
|
||||
{
|
||||
// Incorrect usage, must call RegisterActivator first
|
||||
return E_ILLEGAL_METHOD_CALL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool IsRunningAsUwp()
|
||||
{
|
||||
if (!s_hasCheckedIsRunningAsUwp)
|
||||
{
|
||||
// https://stackoverflow.com/questions/39609643/determine-if-c-application-is-running-as-a-uwp-app-in-desktop-bridge-project
|
||||
UINT32 length;
|
||||
wchar_t packageFamilyName[PACKAGE_FAMILY_NAME_MAX_LENGTH + 1];
|
||||
LONG result = GetPackageFamilyName(GetCurrentProcess(), &length, packageFamilyName);
|
||||
s_isRunningAsUwp = result == ERROR_SUCCESS;
|
||||
s_hasCheckedIsRunningAsUwp = true;
|
||||
}
|
||||
|
||||
return s_isRunningAsUwp;
|
||||
}
|
||||
}
|
||||
|
||||
DesktopNotificationHistoryCompat::DesktopNotificationHistoryCompat(const wchar_t *aumid, ComPtr<IToastNotificationHistory> history)
|
||||
{
|
||||
m_aumid = std::wstring(aumid);
|
||||
m_history = history;
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::Clear()
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->ClearWithId(HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::GetHistory(ABI::Windows::Foundation::Collections::IVectorView<ToastNotification*> **toasts)
|
||||
{
|
||||
ComPtr<IToastNotificationHistory2> history2;
|
||||
RETURN_IF_FAILED(m_history.As(&history2));
|
||||
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return history2->GetHistory(toasts);
|
||||
}
|
||||
else
|
||||
{
|
||||
return history2->GetHistoryWithId(HStringReference(m_aumid.c_str()).Get(), toasts);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::Remove(const wchar_t *tag)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->Remove(HStringReference(tag).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupedTagWithId(HStringReference(tag).Get(), HStringReference(L"").Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::RemoveGroupedTag(const wchar_t *tag, const wchar_t *group)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->RemoveGroupedTag(HStringReference(tag).Get(), HStringReference(group).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupedTagWithId(HStringReference(tag).Get(), HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DesktopNotificationHistoryCompat::RemoveGroup(const wchar_t *group)
|
||||
{
|
||||
if (m_aumid.empty())
|
||||
{
|
||||
return m_history->RemoveGroup(HStringReference(group).Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_history->RemoveGroupWithId(HStringReference(group).Get(), HStringReference(m_aumid.c_str()).Get());
|
||||
}
|
||||
}
|
||||
108
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.h
Normal file
108
Src/Update/DesktopToasts/DesktopNotificationManagerCompat.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE 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 CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <Windows.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <wrl.h>
|
||||
#define TOAST_ACTIVATED_LAUNCH_ARG L"-ToastActivated"
|
||||
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
|
||||
class DesktopNotificationHistoryCompat;
|
||||
|
||||
namespace DesktopNotificationManagerCompat
|
||||
{
|
||||
/// <summary>
|
||||
/// If not running under the Desktop Bridge, you must call this method to register your AUMID with the Compat library and to
|
||||
/// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running
|
||||
/// under Desktop Bridge. Call this upon application startup, before calling any other APIs.
|
||||
/// </summary>
|
||||
/// <param name="aumid">An AUMID that uniquely identifies your application.</param>
|
||||
/// <param name="clsid">The CLSID of your NotificationActivator class.</param>
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid);
|
||||
|
||||
/// <summary>
|
||||
/// Registers your module to handle COM activations. Call this upon application startup.
|
||||
/// </summary>
|
||||
HRESULT RegisterActivator();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notifier. You must have called RegisterActivator first (and also RegisterAumidAndComServer if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
HRESULT CreateToastNotifier(IToastNotifier** notifier);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XmlDocument initialized with the specified string. This is simply a convenience helper method.
|
||||
/// </summary>
|
||||
HRESULT CreateXmlDocumentFromString(const wchar_t *xmlString, ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toast notification. This is simply a convenience helper method.
|
||||
/// </summary>
|
||||
HRESULT CreateToastNotification(ABI::Windows::Data::Xml::Dom::IXmlDocument* content, IToastNotification** notification);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DesktopNotificationHistoryCompat object. You must have called RegisterActivator first (and also RegisterAumidAndComServer if you're a classic Win32 app), or this will throw an exception.
|
||||
/// </summary>
|
||||
HRESULT get_History(std::unique_ptr<DesktopNotificationHistoryCompat>* history);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge.
|
||||
/// </summary>
|
||||
bool CanUseHttpImages();
|
||||
}
|
||||
|
||||
class DesktopNotificationHistoryCompat
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Removes all notifications sent by this app from action center.
|
||||
/// </summary>
|
||||
HRESULT Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all notifications sent by this app that are currently still in Action Center.
|
||||
/// </summary>
|
||||
HRESULT GetHistory(ABI::Windows::Foundation::Collections::IVectorView<ToastNotification*>** history);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an individual toast, with the specified tag label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
HRESULT Remove(const wchar_t *tag);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a toast notification from the action using the notification's tag and group labels.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag label of the toast notification to be removed.</param>
|
||||
/// <param name="group">The group label of the toast notification to be removed.</param>
|
||||
HRESULT RemoveGroupedTag(const wchar_t *tag, const wchar_t *group);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a group of toast notifications, identified by the specified group label, from action center.
|
||||
/// </summary>
|
||||
/// <param name="group">The group label of the toast notifications to be removed.</param>
|
||||
HRESULT RemoveGroup(const wchar_t *group);
|
||||
|
||||
/// <summary>
|
||||
/// Do not call this. Instead, call DesktopNotificationManagerCompat.get_History() to obtain an instance.
|
||||
/// </summary>
|
||||
DesktopNotificationHistoryCompat(const wchar_t *aumid, Microsoft::WRL::ComPtr<IToastNotificationHistory> history);
|
||||
|
||||
private:
|
||||
std::wstring m_aumid;
|
||||
Microsoft::WRL::ComPtr<IToastNotificationHistory> m_history = nullptr;
|
||||
};
|
||||
5
Src/Update/DesktopToasts/DesktopToasts.def
Normal file
5
Src/Update/DesktopToasts/DesktopToasts.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY "DesktopToasts.dll"
|
||||
|
||||
EXPORTS
|
||||
Initialize
|
||||
DisplaySimpleToast
|
||||
59
Src/Update/DesktopToasts/DesktopToasts.h
Normal file
59
Src/Update/DesktopToasts/DesktopToasts.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
using DesktopToastActivateHandler = void(__cdecl*)(void* context, LPCWSTR invokedArgs);
|
||||
|
||||
HRESULT Initialize(LPCWSTR appUserModelId, DesktopToastActivateHandler handler, void* handlerContext);
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text);
|
||||
|
||||
class DesktopToasts
|
||||
{
|
||||
public:
|
||||
explicit DesktopToasts(LPCWSTR appUserModelId)
|
||||
{
|
||||
if (::IsWindows10OrGreater())
|
||||
{
|
||||
auto m_lib = ::LoadLibrary(L"DesktopToasts.dll");
|
||||
if (m_lib)
|
||||
{
|
||||
m_pInitialize = (decltype(m_pInitialize))::GetProcAddress(m_lib, "Initialize");
|
||||
m_pDisplayToast = (decltype(m_pDisplayToast))::GetProcAddress(m_lib, "DisplaySimpleToast");
|
||||
|
||||
if (m_pInitialize && m_pDisplayToast)
|
||||
{
|
||||
if (m_pInitialize(appUserModelId, ToastActivate, this) == S_OK)
|
||||
m_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DesktopToasts()
|
||||
{
|
||||
if (m_lib)
|
||||
::FreeLibrary(m_lib);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text)
|
||||
{
|
||||
return m_pDisplayToast(title, text);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void OnToastActivate(LPCWSTR invokedArgs) {}
|
||||
|
||||
static void __cdecl ToastActivate(void* context, LPCWSTR invokedArgs)
|
||||
{
|
||||
static_cast<DesktopToasts*>(context)->OnToastActivate(invokedArgs);
|
||||
}
|
||||
|
||||
bool m_initialized = false;
|
||||
|
||||
HMODULE m_lib = nullptr;
|
||||
decltype(&::Initialize) m_pInitialize = nullptr;
|
||||
decltype(&::DisplaySimpleToast) m_pDisplayToast = nullptr;
|
||||
};
|
||||
61
Src/Update/DesktopToasts/DesktopToasts.rc
Normal file
61
Src/Update/DesktopToasts/DesktopToasts.rc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION _PRODUCT_VERSION
|
||||
PRODUCTVERSION _PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Open-Shell"
|
||||
VALUE "FileDescription", "Desktop toast notifications support"
|
||||
VALUE "FileVersion", _PRODUCT_VERSION_STR
|
||||
VALUE "InternalName", "DesktopToasts.dll"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2020, The Open-Shell Team"
|
||||
VALUE "OriginalFilename", "DesktopToasts.dll"
|
||||
VALUE "ProductName", "Open-Shell"
|
||||
VALUE "ProductVersion", _PRODUCT_VERSION_STR
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
110
Src/Update/DesktopToasts/DesktopToasts.vcxproj
Normal file
110
Src/Update/DesktopToasts/DesktopToasts.vcxproj
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{d94bd2a6-1872-4f01-b911-f406603aa2e1}</ProjectGuid>
|
||||
<RootNamespace>DesktopToasts</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\Version.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\Version.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>..\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;DESKTOPTOASTS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>DesktopToasts.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;DESKTOPTOASTS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>DesktopToasts.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DesktopNotificationManagerCompat.h" />
|
||||
<ClInclude Include="DesktopToasts.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="DesktopNotificationManagerCompat.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DesktopToasts.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="DesktopToasts.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
43
Src/Update/DesktopToasts/DesktopToasts.vcxproj.filters
Normal file
43
Src/Update/DesktopToasts/DesktopToasts.vcxproj.filters
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DesktopNotificationManagerCompat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DesktopToasts.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DesktopNotificationManagerCompat.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DesktopToasts.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="DesktopToasts.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
131
Src/Update/DesktopToasts/dllmain.cpp
Normal file
131
Src/Update/DesktopToasts/dllmain.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#include <windows.h>
|
||||
#include <NotificationActivationCallback.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
|
||||
#include "DesktopToasts.h"
|
||||
#include "DesktopNotificationManagerCompat.h"
|
||||
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT _hrTemp = hr; if (FAILED(_hrTemp)) { return _hrTemp; } } while (false)
|
||||
|
||||
using namespace ABI::Windows::Data::Xml::Dom;
|
||||
using namespace ABI::Windows::UI::Notifications;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
DesktopToastActivateHandler g_handler = nullptr;
|
||||
void* g_handlerContext = nullptr;
|
||||
|
||||
class DECLSPEC_UUID("E407B70A-1FBD-4D5E-8822-231C69102472") NotificationActivator WrlSealed WrlFinal
|
||||
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE Activate(
|
||||
_In_ LPCWSTR appUserModelId,
|
||||
_In_ LPCWSTR invokedArgs,
|
||||
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA * data,
|
||||
ULONG dataCount) override
|
||||
{
|
||||
if (g_handler)
|
||||
g_handler(g_handlerContext, invokedArgs);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// Flag class as COM creatable
|
||||
CoCreatableClass(NotificationActivator);
|
||||
|
||||
HRESULT Initialize(LPCWSTR appUserModelId, DesktopToastActivateHandler handler, void* handlerContext)
|
||||
{
|
||||
RETURN_IF_FAILED(DesktopNotificationManagerCompat::RegisterAumidAndComServer(appUserModelId, __uuidof(NotificationActivator)));
|
||||
RETURN_IF_FAILED(DesktopNotificationManagerCompat::RegisterActivator());
|
||||
g_handler = handler;
|
||||
g_handlerContext = handlerContext;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT SetNodeValueString(HSTRING inputString, IXmlNode* node, IXmlDocument* xml)
|
||||
{
|
||||
ComPtr<IXmlText> inputText;
|
||||
RETURN_IF_FAILED(xml->CreateTextNode(inputString, &inputText));
|
||||
|
||||
ComPtr<IXmlNode> inputTextNode;
|
||||
RETURN_IF_FAILED(inputText.As(&inputTextNode));
|
||||
|
||||
ComPtr<IXmlNode> appendedChild;
|
||||
return node->AppendChild(inputTextNode.Get(), &appendedChild);
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
HRESULT SetTextValues(const PCWSTR* textValues, UINT32 textValuesCount, IXmlDocument* toastXml)
|
||||
{
|
||||
ComPtr<IXmlNodeList> nodeList;
|
||||
RETURN_IF_FAILED(toastXml->GetElementsByTagName(HStringReference(L"text").Get(), &nodeList));
|
||||
|
||||
UINT32 nodeListLength;
|
||||
RETURN_IF_FAILED(nodeList->get_Length(&nodeListLength));
|
||||
|
||||
// If a template was chosen with fewer text elements, also change the amount of strings
|
||||
// passed to this method.
|
||||
RETURN_IF_FAILED(textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG);
|
||||
|
||||
for (UINT32 i = 0; i < textValuesCount; i++)
|
||||
{
|
||||
ComPtr<IXmlNode> textNode;
|
||||
RETURN_IF_FAILED(nodeList->Item(i, &textNode));
|
||||
|
||||
RETURN_IF_FAILED(SetNodeValueString(HStringReference(textValues[i]).Get(), textNode.Get(), toastXml));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT DisplaySimpleToast(LPCWSTR title, LPCWSTR text)
|
||||
{
|
||||
// Construct XML
|
||||
ComPtr<IXmlDocument> doc;
|
||||
HRESULT hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(L"<toast><visual><binding template='ToastGeneric'><text></text><text></text></binding></visual></toast>", &doc);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
PCWSTR textValues[] = { title, text };
|
||||
SetTextValues(textValues, ARRAYSIZE(textValues), doc.Get());
|
||||
|
||||
// Create the notifier
|
||||
// Classic Win32 apps MUST use the compat method to create the notifier
|
||||
ComPtr<IToastNotifier> notifier;
|
||||
hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Create the notification itself (using helper method from compat library)
|
||||
ComPtr<IToastNotification> toast;
|
||||
hr = DesktopNotificationManagerCompat::CreateToastNotification(doc.Get(), &toast);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// And show it!
|
||||
hr = notifier->Show(toast.Get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user