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

Get Icon from process #2330

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
70 changes: 58 additions & 12 deletions dev/AppNotifications/AppNotificationUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

#include "AppNotificationUtility.h"
#include <winrt/Windows.ApplicationModel.Core.h>
#include <winrt/Windows.Foundation.h>
Expand All @@ -15,12 +15,15 @@
#include "NotificationProgressData.h"

#include <Microsoft.Foundation.String.h>

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

namespace std
{
using namespace std::filesystem;
}

namespace winrt
{
Expand Down Expand Up @@ -237,14 +240,57 @@ std::wstring Microsoft::Windows::AppNotifications::Helpers::GetDisplayNameBasedO
return displayName;
}

// Placeholder
HRESULT RetrieveAssetsFromProcess(_Out_ Microsoft::Windows::AppNotifications::Helpers::AppNotificationAssets& /*assets*/)
inline wil::unique_hicon RetrieveIconFromProcess()
{
// THROW_HR_IF_MSG(E_UNEXPECTED, VerifyIconFileExtension(iconFilePath));
std::wstring processPath{};
THROW_IF_FAILED(wil::GetModuleFileNameExW(GetCurrentProcess(), nullptr, processPath));

return E_NOTIMPL;
// Extract the small icon from the first .ico, if failed extract the large icon.
wil::unique_hicon hIcon{};
if (!ExtractIconExW(processPath.c_str(), 0 /* index */, nullptr /* Large icon */, &hIcon, 1))
{
THROW_HR_IF(E_FAIL, ExtractIconExW(processPath.c_str(), 0, &hIcon, nullptr /* Small Icon */, 1) == 0);
}

return hIcon;
}

inline std::wstring RetrieveLocalFolderPath()
{
wil::unique_cotaskmem_string localAppDataPath;
THROW_IF_FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0 /* flags */, nullptr /* access token handle */, &localAppDataPath));

// path: C:\Users\<currentUser>\AppData\Local\Microsoft
std::path localFolderPath{ std::wstring(localAppDataPath.get()) + Microsoft::Windows::AppNotifications::Helpers::c_localMicrosoftFolder };
THROW_HR_IF(ERROR_FILE_NOT_FOUND, !std::exists(localFolderPath));

// path: C:\Users\<currentUser>\AppData\Local\Microsoft\WindowsAppSDK
localFolderPath.append(Microsoft::Windows::AppNotifications::Helpers::c_localWindowsAppSDKFolder);
if (!std::exists(localFolderPath))
{
std::create_directory(localFolderPath);
}

return std::wstring(localFolderPath.c_str());
}

HRESULT Microsoft::Windows::AppNotifications::Helpers::RetrieveAssetsFromProcess(_Out_ Microsoft::Windows::AppNotifications::Helpers::AppNotificationAssets& assets) noexcept try
{
wil::unique_hicon hIcon{ RetrieveIconFromProcess() };

std::wstring notificationAppId{ RetrieveNotificationAppId() };

// path: C:\Users\<currentUser>\AppData\Local\Microsoft\WindowsAppSDK\{AppGUID}.png
std::wstring writeToFile{ RetrieveLocalFolderPath().c_str() + c_backSlash + notificationAppId + c_pngExtension };
Microsoft::Windows::AppNotifications::WICHelpers::WriteHIconToPngFile(hIcon, writeToFile.c_str());

assets.displayName = Microsoft::Windows::AppNotifications::Helpers::GetDisplayNameBasedOnProcessName();
assets.iconFilePath = writeToFile;

return S_OK;
}
CATCH_RETURN()

// Do nothing. This is just a placeholder while the UDK is ingested with the proper API.
HRESULT ToastNotifications_RetrieveAssets_Stub(_Out_ Microsoft::Windows::AppNotifications::Helpers::AppNotificationAssets& /*assets*/)
{
Expand Down Expand Up @@ -300,8 +346,8 @@ void Microsoft::Windows::AppNotifications::Helpers::RegisterAssets(std::wstring
// 3. Use the default assets.
Microsoft::Windows::AppNotifications::Helpers::AppNotificationAssets assets{};

if (FAILED(RetrieveAssetsFromProcess(assets)) &&
FAILED(ToastNotifications_RetrieveAssets_Stub(assets)))
if (FAILED(ToastNotifications_RetrieveAssets_Stub(assets)) &&
FAILED(RetrieveAssetsFromProcess(assets)))
{
THROW_IF_FAILED(RetrieveDefaultAssets(assets));
}
Expand Down
6 changes: 6 additions & 0 deletions 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)" };

struct AppNotificationAssets {
std::wstring displayName;
Expand Down Expand Up @@ -67,4 +71,6 @@ namespace Microsoft::Windows::AppNotifications::Helpers
winrt::Microsoft::Windows::AppNotifications::AppNotification ToastNotificationFromToastProperties(ABI::Microsoft::Internal::ToastNotifications::INotificationProperties* properties);

std::wstring GetDisplayNameBasedOnProcessName();

HRESULT RetrieveAssetsFromProcess(_Out_ Microsoft::Windows::AppNotifications::Helpers::AppNotificationAssets& assets) noexcept;
}
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)WICUtility.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)WICUtility.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="$(MSBuildThisFileDirectory)AppNotifications.idl" />
Expand Down
165 changes: 165 additions & 0 deletions dev/AppNotifications/WICUtility.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "pch.h"
#include "WICUtility.h"
#include <cwctype>
#include <winrt/Windows.Foundation.h>
#include <winrt/base.h>
#include <externs.h>

#include <ShObjIdl_core.h>
#include <wincodec.h>
#include <Ocidl.h>
#include <windows.h>

typedef enum
{
BMPV_1,
BMPV_5,
} BITMAP_VERSION;

winrt::com_ptr<IWICBitmapSource> ConvertWICBitmapPixelFormat(
winrt::com_ptr<IWICImagingFactory> const& wicImagingFactory,
winrt::com_ptr<IWICBitmapSource> const& wicBitmapSource,
WICPixelFormatGUID guidPixelFormatSource,
WICBitmapDitherType bitmapDitherType)
{
winrt::com_ptr<IWICFormatConverter> wicFormatConverter;
THROW_IF_FAILED(wicImagingFactory->CreateFormatConverter(wicFormatConverter.put()));

THROW_IF_FAILED(wicFormatConverter->Initialize(wicBitmapSource.get(), guidPixelFormatSource, bitmapDitherType, nullptr, 0.f, WICBitmapPaletteTypeCustom));

// Store the converted bitmap as ppToRenderBitmapSource
winrt::com_ptr<IWICBitmapSource> wicBitmapSourceConverted;
THROW_IF_FAILED(wicFormatConverter->QueryInterface(_uuidof(IWICBitmapSource), wicBitmapSourceConverted.put_void()));

return wicBitmapSourceConverted;
}

void AddFrameToWICBitmap(
winrt::com_ptr<IWICImagingFactory> const& wicImagingFactory,
winrt::com_ptr<IWICBitmapEncoder> const& wicEncoder,
winrt::com_ptr<IWICBitmapSource> const& wicBitmapSource,
BITMAP_VERSION bmpv)
{
winrt::com_ptr<IWICBitmapFrameEncode> wicFrameEncoder;
winrt::com_ptr<IPropertyBag2> wicEncoderOptions;

THROW_IF_FAILED(wicEncoder->CreateNewFrame(wicFrameEncoder.put(), wicEncoderOptions.put()));

GUID containerGuid;
THROW_IF_FAILED(wicEncoder->GetContainerFormat(&containerGuid));

if ((containerGuid == GUID_ContainerFormatBmp) && (bmpv == BMPV_5))
{
// Write the encoder option to the property bag instance.
VARIANT varValue{};

varValue.vt = VT_BOOL;
varValue.boolVal = VARIANT_TRUE;

// Options to enable the v5 header support for 32bppBGRA.
PROPBAG2 v5HeaderOption{};

std::wstring str = L"EnableV5Header32bppBGRA";
v5HeaderOption.pstrName = (LPOLESTR)str.c_str();

THROW_IF_FAILED(wicEncoderOptions->Write(1, &v5HeaderOption, &varValue));
}

THROW_IF_FAILED(wicFrameEncoder->Initialize(wicEncoderOptions.get()));

UINT uWidth, uHeight;
THROW_IF_FAILED(wicBitmapSource->GetSize(&uWidth, &uHeight));

THROW_IF_FAILED(wicFrameEncoder->SetSize(uWidth, uHeight));

winrt::com_ptr<IWICBitmapSource> wicbitmapSourceConverted;
wicbitmapSourceConverted = ConvertWICBitmapPixelFormat(wicImagingFactory, wicBitmapSource, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone);

WICRect rect { 0, 0, static_cast<INT>(uWidth), static_cast<INT>(uHeight) };

// Write the image data and commit
THROW_IF_FAILED(wicFrameEncoder->WriteSource(wicbitmapSourceConverted.get(), &rect));

THROW_IF_FAILED(wicFrameEncoder->Commit());
}

winrt::com_ptr<IStream> GetStreamOfWICBitmapSource(
winrt::com_ptr<IWICImagingFactory> const& wicImagingFactory,
winrt::com_ptr<IWICBitmapSource> const& wicBitmapSource,
_In_ REFGUID guidContainerFormat,
_In_ BITMAP_VERSION bmpv)
{
winrt::com_ptr<IStream> spImageStream;
THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr /* handle */, true /* delete on release */, spImageStream.put()));

// Create encoder and initialize it
winrt::com_ptr<IWICBitmapEncoder> wicEncoder;
THROW_IF_FAILED(wicImagingFactory->CreateEncoder(guidContainerFormat, nullptr, wicEncoder.put()));

THROW_IF_FAILED(wicEncoder->Initialize(spImageStream.get(), WICBitmapEncoderCacheOption::WICBitmapEncoderNoCache));

// Add a single frame to the encoder with the Bitmap
AddFrameToWICBitmap(wicImagingFactory, wicEncoder, wicBitmapSource, bmpv);

THROW_IF_FAILED(wicEncoder->Commit());

// Seek the stream to the beginning and transfer
static const LARGE_INTEGER lnBeginning = {};
THROW_IF_FAILED(spImageStream->Seek(lnBeginning, STREAM_SEEK_SET, nullptr /* new seek pointer */));

return spImageStream;
}

void SaveImageWithWIC(
winrt::com_ptr<IWICImagingFactory> const& wicImagingFactory,
winrt::com_ptr<IWICBitmapSource> const& wicBitmapSource,
_In_ REFGUID guidContainerFormat,
winrt::com_ptr<IStream>& pStream)
{
winrt::com_ptr<IStream> spImageStream;
spImageStream = GetStreamOfWICBitmapSource(wicImagingFactory, wicBitmapSource, guidContainerFormat, BMPV_1);

// Seek the stream to the beginning and transfer
static LARGE_INTEGER const lnBeginning{ 0 };
THROW_IF_FAILED(spImageStream->Seek(lnBeginning, STREAM_SEEK_SET, nullptr /* new seek pointer */));

static ULARGE_INTEGER lnbuffer{ INT_MAX };
THROW_IF_FAILED(spImageStream->CopyTo(pStream.get(), lnbuffer, nullptr /* pointer to number of bytes read */, nullptr /* pointer to number of bytes written */));
}

void Microsoft::Windows::AppNotifications::WICHelpers::WriteHIconToPngFile(wil::unique_hicon const& hIcon, _In_ PCWSTR pszFileName)
{
auto wicImagingFactory{ winrt::create_instance<IWICImagingFactory>(CLSID_WICImagingFactory, CLSCTX_INPROC_SERVER) };

winrt::com_ptr<IWICBitmap> wicBitmap;
THROW_IF_FAILED(wicImagingFactory->CreateBitmapFromHICON(hIcon.get(), wicBitmap.put()));

// Create stream to save the HICON to.
winrt::com_ptr<IStream> spStream;
THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr /* handle */, TRUE, spStream.put()));

winrt::com_ptr<IWICBitmapSource> wicBitmapSource;
wicBitmapSource = wicBitmap;

SaveImageWithWIC(wicImagingFactory, wicBitmapSource, GUID_ContainerFormatPng, spStream);

// Write out the stream to a file.
winrt::com_ptr<IStream> spStreamOut;
THROW_IF_FAILED(SHCreateStreamOnFileEx(pszFileName, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, spStreamOut.put()));

STATSTG statstg;
THROW_IF_FAILED(spStream->Stat(&statstg, STATFLAG_NONAME));

THROW_IF_FAILED(IStream_Reset(spStream.get()));

THROW_IF_FAILED(spStreamOut->SetSize(statstg.cbSize));

// TODO: Comments need to be added
THROW_IF_FAILED(spStream->CopyTo(spStreamOut.get(), statstg.cbSize, nullptr /* pointer to number of bytes read */, nullptr /* pointer to number of bytes written */));

THROW_IF_FAILED(spStreamOut->Commit(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE));
}
12 changes: 12 additions & 0 deletions dev/AppNotifications/WICUtility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#pragma once

#include "pch.h"

namespace Microsoft::Windows::AppNotifications::WICHelpers
{
void WriteHIconToPngFile(wil::unique_hicon const& hIcon, _In_ PCWSTR pszFileName);
}

Loading