Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifications assets full support #2295

Merged
merged 12 commits into from
Apr 1, 2022
14 changes: 8 additions & 6 deletions WindowsAppRuntime.sln
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,13 @@ EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AccessControlTestAppPackage", "test\TestApps\AccessControlTestAppPackage\AccessControlTestAppPackage.wapproj", "{03EBF097-66C6-4996-95A3-28F6F5999E27}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToastNotificationsDemoApp", "test\TestApps\ToastNotificationsDemoApp\ToastNotificationsDemoApp.vcxproj", "{412D023E-8635-4AD2-A0EA-E19E08D36915}"
ProjectSection(ProjectDependencies) = postProject
{B73AD907-6164-4294-88FB-F3C9C10DA1F1} = {B73AD907-6164-4294-88FB-F3C9C10DA1F1}
{A7391725-4EF5-438F-8DE1-645423E46955} = {A7391725-4EF5-438F-8DE1-645423E46955}
{9C1A6C58-52D6-4514-9120-5C339C5DF4BE} = {9C1A6C58-52D6-4514-9120-5C339C5DF4BE}
{B71E818A-882E-456A-87E5-4DE4A6602B99} = {B71E818A-882E-456A-87E5-4DE4A6602B99}
{C422B090-F501-4204-8068-1084B0799405} = {C422B090-F501-4204-8068-1084B0799405}
EndProjectSection
EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "ToastNotificationsDemoAppPackage", "test\TestApps\ToastNotificationsDemoAppPackage\ToastNotificationsDemoAppPackage.wapproj", "{E695A08E-8735-41CD-AE55-A5B589BA297F}"
EndProject
Expand All @@ -301,6 +308,7 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppNotifications", "dev\AppNotifications\AppNotifications.vcxitems", "{B4824897-88E0-4927-8FB9-E60106F01ED9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Windows.AppNotifications.Projection", "dev\Projections\CS\Microsoft.Windows.AppNotifications.Projection\Microsoft.Windows.AppNotifications.Projection.csproj", "{2385A443-A1C2-4562-8D28-D0AD239EE5E7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{82A73181-EA4A-431A-B82B-BE6734604CC9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test_Common", "test\Common\Common.vcxproj", "{50451390-66E7-4465-8804-427560625794}"
Expand Down Expand Up @@ -1270,10 +1278,6 @@ Global
{BC5E5A3E-E733-4388-8B00-F8495DA7C778} = {3DE93B2F-F887-437D-B512-6B1024ABA290}
{34671779-4A4D-4D0E-B259-CD0F14D4F6D4} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
{885A43FA-052D-4B0D-A2DC-13EE15796435} = {34671779-4A4D-4D0E-B259-CD0F14D4F6D4}
{BC5E5A3E-E733-4388-8B00-F8495DA7C778} = {3DE93B2F-F887-437D-B512-6B1024ABA290}
{BC5E5A3E-E733-4388-8B00-F8495DA7C778} = {3DE93B2F-F887-437D-B512-6B1024ABA290}
{34671779-4A4D-4D0E-B259-CD0F14D4F6D4} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
{885A43FA-052D-4B0D-A2DC-13EE15796435} = {34671779-4A4D-4D0E-B259-CD0F14D4F6D4}
{A06B7FE8-D201-4EA2-BB10-61B4595EC377} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
{C91BCB93-9ED1-4ACD-85F3-26F9F6AC52E3} = {A06B7FE8-D201-4EA2-BB10-61B4595EC377}
{08BC78E0-63C6-49A7-81B3-6AFC3DEAC4DE} = {8630F7AA-2969-4DC9-8700-9B468C1DC21D}
Expand All @@ -1284,8 +1288,6 @@ Global
{1C9A0791-2BAA-420B-84B6-C0721F22A6E8} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
{B4824897-88E0-4927-8FB9-E60106F01ED9} = {1C9A0791-2BAA-420B-84B6-C0721F22A6E8}
{2385A443-A1C2-4562-8D28-D0AD239EE5E7} = {716C26A0-E6B0-4981-8412-D14A4D410531}
{34671779-4A4D-4D0E-B259-CD0F14D4F6D4} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
{885A43FA-052D-4B0D-A2DC-13EE15796435} = {34671779-4A4D-4D0E-B259-CD0F14D4F6D4}
{82A73181-EA4A-431A-B82B-BE6734604CC9} = {8630F7AA-2969-4DC9-8700-9B468C1DC21D}
{50451390-66E7-4465-8804-427560625794} = {82A73181-EA4A-431A-B82B-BE6734604CC9}
{0419CA2B-5ED1-49F0-B70B-5F470A15D3D0} = {448ED2E5-0B37-4D97-9E6B-8C10A507976A}
Expand Down
7 changes: 7 additions & 0 deletions dev/AppNotifications/AppNotificationManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <string_view>
#include <winrt/Windows.Foundation.Collections.h>
#include <WindowsAppRuntime.SelfContained.h>
#include <ShellLocalization.h>

using namespace std::literals;

Expand All @@ -46,6 +47,7 @@ namespace PushNotificationHelpers
}

using namespace Microsoft::Windows::AppNotifications::Helpers;
using namespace Microsoft::Windows::AppNotifications::ShellLocalization;

namespace winrt::Microsoft::Windows::AppNotifications::implementation
{
Expand Down Expand Up @@ -220,6 +222,11 @@ namespace winrt::Microsoft::Windows::AppNotifications::implementation

if (!AppModel::Identity::IsPackagedProcess())
{
// If the app icon was inferred from process, then we should clean it up.
// Do not fail this function if such a file doesn't exist.
danielayala94 marked this conversation as resolved.
Show resolved Hide resolved
winrt::hresult deleteIconResult{ DeleteIconFromCache() };
THROW_HR_IF(deleteIconResult, FAILED(deleteIconResult) && deleteIconResult != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));

std::wstring storedComActivatorString;
THROW_IF_FAILED(GetActivatorGuid(storedComActivatorString));
UnRegisterComServer(storedComActivatorString);
Expand Down
130 changes: 47 additions & 83 deletions dev/AppNotifications/AppNotificationUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "pch.h"
#include <cwctype>

#include "AppNotificationUtility.h"
#include <winrt/Windows.ApplicationModel.Core.h>
#include <winrt/Windows.Foundation.h>
Expand All @@ -13,11 +14,17 @@
#include "AppNotification.h"
#include "NotificationProgressData.h"

#include <wil/resource.h>
#include <wil/win32_helpers.h>
#include <propkey.h> // PKEY properties
#include <propsys.h> // IPropertyStore
#include <Microsoft.Foundation.String.h>
#include <ShObjIdl_core.h>
#include <shlobj_core.h>
#include <filesystem>

#include <ShellLocalization.h>

namespace std
{
using namespace std::filesystem;
}

namespace winrt
{
Expand Down Expand Up @@ -216,7 +223,7 @@ HRESULT Microsoft::Windows::AppNotifications::Helpers::GetActivatorGuid(std::wst
}
CATCH_RETURN()

std::wstring Microsoft::Windows::AppNotifications::Helpers::SetDisplayNameBasedOnProcessName()
std::wstring Microsoft::Windows::AppNotifications::Helpers::GetDisplayNameBasedOnProcessName()
{
std::wstring displayName{};
THROW_IF_FAILED(wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr, displayName));
Expand All @@ -232,82 +239,6 @@ std::wstring Microsoft::Windows::AppNotifications::Helpers::SetDisplayNameBasedO
return displayName;
}

void Microsoft::Windows::AppNotifications::Helpers::RegisterAssets(std::wstring const& appId, std::wstring const& clsid)
{
wil::unique_hkey hKey;
// subKey: \Software\Classes\AppUserModelId\{AppGUID}
std::wstring subKey{ c_appIdentifierPath + appId };

THROW_IF_WIN32_ERROR(RegCreateKeyEx(
HKEY_CURRENT_USER,
subKey.c_str(),
0,
nullptr /* lpClass */,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
nullptr /* lpSecurityAttributes */,
&hKey,
nullptr /* lpdwDisposition */));

// Retrieve the display name
std::wstring displayName{};
displayName = SetDisplayNameBasedOnProcessName();

// Retrieve the icon
std::wstring iconFilePath{};

wil::unique_hwnd hWindow{ GetConsoleWindow() };

if (hWindow)
{
// Retrieve DisplayName and IconUri
// - DisplayName: Retrieve from Shell. If not specified, fall back to filename.
// - Icon: Retrieve from Shell. If it's not the case or the file extension is unsupported, then throw.
winrt::com_ptr<IPropertyStore> propertyStore;
THROW_IF_FAILED(SHGetPropertyStoreForWindow(hWindow.get(), IID_PPV_ARGS(propertyStore.put())));

wil::unique_prop_variant propVariantDisplayName;
// Do not throw in case of failure, default to the filepath approach below as fallback to set a DisplayName.
THROW_IF_FAILED(propertyStore->GetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, &propVariantDisplayName));

if (propVariantDisplayName.vt == VT_LPWSTR && propVariantDisplayName.pwszVal != nullptr && wcslen(propVariantDisplayName.pwszVal) > 0)
{
displayName = propVariantDisplayName.pwszVal;
}

wil::unique_prop_variant propVariantIcon;
THROW_IF_FAILED(propertyStore->GetValue(PKEY_AppUserModel_RelaunchIconResource, &propVariantIcon));

THROW_HR_IF_MSG(E_UNEXPECTED, (propVariantIcon.vt == VT_EMPTY || propVariantIcon.pwszVal == nullptr), "Icon is not specified");

THROW_HR_IF_MSG(E_UNEXPECTED, propVariantIcon.vt != VT_LPWSTR, "Icon should be a valid Unicode string");

THROW_HR_IF_MSG(E_UNEXPECTED, wcslen(propVariantIcon.pwszVal) == 0, "Icon is an empty string");

iconFilePath = propVariantIcon.pwszVal;

// Icon filepaths from Shell APIs have this format: <filepath>,-<index>,
// since .ico files can have multiple icons in the same file.
// NotificationController doesn't seem to support such format, so let it take the first icon by default.
auto iteratorForCommaDelimiter{ iconFilePath.find_first_of(L",") };
if (iteratorForCommaDelimiter != std::wstring::npos) // It may or may not have an index, which is fine.
{
iconFilePath.erase(iteratorForCommaDelimiter);
}

auto iteratorForFileExtension{ iconFilePath.find_first_of(L".") };
THROW_HR_IF_MSG(E_UNEXPECTED, iteratorForFileExtension == std::wstring::npos, "You must provide a valid filepath as the app icon.");

std::wstring iconFileExtension{ iconFilePath.substr(iteratorForFileExtension) };
THROW_HR_IF_MSG(E_UNEXPECTED, iconFileExtension != L".ico" && iconFileExtension != L".png",
"You must provide a supported file extension as the icon (.ico or .png).");
}

RegisterValue(hKey, L"DisplayName", reinterpret_cast<const BYTE*>(displayName.c_str()), REG_EXPAND_SZ, displayName.size() * sizeof(wchar_t));
RegisterValue(hKey, L"IconUri", reinterpret_cast<const BYTE*>(iconFilePath.c_str()), REG_EXPAND_SZ, iconFilePath.size() * sizeof(wchar_t));
RegisterValue(hKey, L"CustomActivator", reinterpret_cast<const BYTE*>(clsid.c_str()), REG_SZ, clsid.size() * sizeof(wchar_t));
}

winrt::guid Microsoft::Windows::AppNotifications::Helpers::RegisterComActivatorGuidAndAssets()
{
std::wstring registeredGuid;
Expand All @@ -331,12 +262,45 @@ winrt::guid Microsoft::Windows::AppNotifications::Helpers::RegisterComActivatorG
THROW_IF_FAILED(hr);
}

std::wstring notificationAppId{ RetrieveNotificationAppId() };
std::wstring notificationAppId{ Microsoft::Windows::AppNotifications::Helpers::RetrieveNotificationAppId() };
RegisterAssets(notificationAppId, registeredGuid);

// Remove braces around the guid string
return winrt::guid(registeredGuid.substr(1, registeredGuid.size() - 2));

}

void Microsoft::Windows::AppNotifications::Helpers::RegisterAssets(std::wstring const& appId, std::wstring const& clsid)
{
wil::unique_hkey hKey;
// subKey: \Software\Classes\AppUserModelId\{AppGUID}
std::wstring subKey{ c_appIdentifierPath + appId };

THROW_IF_WIN32_ERROR(RegCreateKeyEx(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does WIL have registry functions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it does. But that would be very nice to have :)

HKEY_CURRENT_USER,
subKey.c_str(),
0,
nullptr /* lpClass */,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
nullptr /* lpSecurityAttributes */,
&hKey,
nullptr /* lpdwDisposition */));

// Try the following techniques to retrieve display name and icon:
// 1. From the current process.
// 2. Based on the best app shortcut, using the FrameworkUdk.
// 3. Use the default assets.
Microsoft::Windows::AppNotifications::ShellLocalization::AppNotificationAssets assets{};

if (FAILED(Microsoft::Windows::AppNotifications::ShellLocalization::RetrieveAssetsFromShortcut(assets)) &&
FAILED(Microsoft::Windows::AppNotifications::ShellLocalization::RetrieveAssetsFromProcess(assets)))
{
THROW_IF_FAILED(Microsoft::Windows::AppNotifications::ShellLocalization::RetrieveDefaultAssets(assets));
}

RegisterValue(hKey, L"DisplayName", reinterpret_cast<const BYTE*>(assets.displayName.c_str()), REG_EXPAND_SZ, assets.displayName.size() * sizeof(wchar_t));
RegisterValue(hKey, L"IconUri", reinterpret_cast<const BYTE*>(assets.iconFilePath.c_str()), REG_EXPAND_SZ, assets.iconFilePath.size() * sizeof(wchar_t));
danielayala94 marked this conversation as resolved.
Show resolved Hide resolved
RegisterValue(hKey, L"CustomActivator", reinterpret_cast<const BYTE*>(clsid.c_str()), REG_SZ, wil::guid_string_length * sizeof(wchar_t));
}

wil::unique_cotaskmem_string Microsoft::Windows::AppNotifications::Helpers::ConvertUtf8StringToWideString(unsigned long length, const byte* utf8String)
Expand Down
6 changes: 5 additions & 1 deletion dev/AppNotifications/AppNotificationUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ namespace Microsoft::Windows::AppNotifications::Helpers
const std::wstring c_appIdentifierPath{ LR"(Software\Classes\AppUserModelId\)" };
const std::wstring c_clsIdPath{ LR"(Software\Classes\CLSID\)" };
const std::wstring c_quote{ LR"(")" };
const std::wstring c_backSlash{ LR"(\)" };
const std::wstring c_notificationActivatedArgument{ L" ----AppNotificationActivated:" };
const std::wstring c_localMicrosoftFolder{ LR"(\Microsoft)" };
const std::wstring c_localWindowsAppSDKFolder{ LR"(\WindowsAppSDK)" };
const std::wstring c_pngExtension{ LR"(.png)" };

inline const int GUID_LENGTH = 39; // GUID + '{' + '}' + '/0'

Expand Down Expand Up @@ -61,5 +65,5 @@ namespace Microsoft::Windows::AppNotifications::Helpers

winrt::Microsoft::Windows::AppNotifications::AppNotification ToastNotificationFromToastProperties(ABI::Microsoft::Internal::ToastNotifications::INotificationProperties* properties);

danielayala94 marked this conversation as resolved.
Show resolved Hide resolved
std::wstring SetDisplayNameBasedOnProcessName();
std::wstring GetDisplayNameBasedOnProcessName();
}
2 changes: 2 additions & 0 deletions dev/AppNotifications/AppNotifications.vcxitems
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ClInclude Include="$(MSBuildThisFileDirectory)NotificationProperties.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)NotificationTransientProperties.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)NotificationProgressData.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)ShellLocalization.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)AppNotificationManager.cpp" />
Expand All @@ -32,6 +33,7 @@
<ClCompile Include="$(MSBuildThisFileDirectory)NotificationProperties.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)NotificationTransientProperties.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)NotificationProgressData.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)ShellLocalization.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="$(MSBuildThisFileDirectory)AppNotifications.idl" />
Expand Down
Loading