Skip to content

Commit

Permalink
Get Icon from process (#2330)
Browse files Browse the repository at this point in the history
* Get Icon from process

* Address comments
  • Loading branch information
sharath2727 authored Mar 31, 2022
1 parent 13a45df commit 062c2b8
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 47 deletions.
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

0 comments on commit 062c2b8

Please sign in to comment.