Dramatically simplify Icon

This commit is contained in:
Dustin L. Howett 2025-08-19 16:48:35 -05:00
parent 7055b99acc
commit 79cf5e2c74
2 changed files with 42 additions and 258 deletions

View File

@ -259,23 +259,18 @@ static UINT ConExtractIconInBothSizesW(PCWSTR szFileName, int nIconIndex, HICON*
}
// Excerpted Region Ends
Icon::Icon() :
_fInitialized(false),
_hDefaultIcon(nullptr),
_hDefaultSmIcon(nullptr),
_hIcon(nullptr),
_hSmIcon(nullptr)
Icon::Icon()
{
}
Icon::~Icon()
{
// Do not destroy icon handles. They're shared icons as they were loaded from LoadIcon/LoadImage.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648063(v=vs.85).aspx
// Do destroy icons from ExtractIconEx. They're not shared.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069(v=vs.85).aspx
_DestroyNonDefaultIcons();
#pragma warning(push)
#pragma warning(disable : 4302) // typecast warning from MAKEINTRESOURCE
_hDefaultIcon = LoadIconW(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));
_hDefaultSmIcon = (HICON)LoadImageW(nullptr,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_SHARED);
#pragma warning(pop)
}
// Routine Description:
@ -299,62 +294,33 @@ Icon& Icon::Instance()
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::GetIcons(_Out_opt_ HICON* const phIcon, _Out_opt_ HICON* const phSmIcon)
{
auto hr = S_OK;
if (nullptr != phIcon)
if (phIcon)
{
hr = _GetAvailableIconFromReference(_hIcon, _hDefaultIcon, phIcon);
}
if (SUCCEEDED(hr))
{
if (nullptr != phSmIcon)
*phIcon = _hIcon.get();
if (!*phIcon)
{
hr = _GetAvailableIconFromReference(_hSmIcon, _hDefaultSmIcon, phSmIcon);
*phIcon = _hDefaultIcon;
}
if (!*phIcon)
{
return E_FAIL;
}
}
return hr;
}
// Routine Description:
// - Sets custom icons onto the class or resets the icons to defaults. Use a null handle to reset an icon to its default value.
// Arguments:
// - hIcon - The large icon handle or null to reset to default
// - hSmIcon - The small icon handle or null to reset to default
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::SetIcons(const HICON hIcon, const HICON hSmIcon)
{
auto hr = _SetIconFromReference(_hIcon, hIcon);
if (SUCCEEDED(hr))
if (phSmIcon)
{
hr = _SetIconFromReference(_hSmIcon, hSmIcon);
}
if (SUCCEEDED(hr))
{
HICON hNewIcon;
HICON hNewSmIcon;
hr = GetIcons(&hNewIcon, &hNewSmIcon);
if (SUCCEEDED(hr))
*phSmIcon = _hSmIcon.get();
if (!*phSmIcon)
{
// Special case. If we had a non-default big icon and a default small icon, set the small icon to null when updating the window.
// This will cause the large one to be stretched and used as the small one.
if (hNewIcon != _hDefaultIcon && hNewSmIcon == _hDefaultSmIcon)
{
hNewSmIcon = nullptr;
}
PostMessageW(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), WM_SETICON, ICON_BIG, (LPARAM)hNewIcon);
PostMessageW(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), WM_SETICON, ICON_SMALL, (LPARAM)hNewSmIcon);
*phSmIcon = _hDefaultSmIcon;
}
if (!*phIcon)
{
return E_FAIL;
}
}
return hr;
return S_OK;
}
// Routine Description:
@ -373,10 +339,10 @@ Icon& Icon::Instance()
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069(v=vs.85).aspx
ConExtractIconInBothSizesW(pwszIconLocation, nIconIndex, &_hIcon, &_hSmIcon);
// If the large icon failed, then ensure that we use the defaults.
if (_hIcon == nullptr)
// If the large icon failed, clear the other one as well.
if (!_hIcon)
{
_DestroyNonDefaultIcons(); // ensure handles are destroyed/null
_hSmIcon.reset();
hr = E_FAIL;
}
@ -394,182 +360,12 @@ Icon& Icon::Instance()
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::ApplyWindowMessageWorkaround(const HWND hwnd)
{
HICON hIcon;
HICON hSmIcon;
HICON hIcon, hSmIcon;
auto hr = GetIcons(&hIcon, &hSmIcon);
RETURN_IF_FAILED(GetIcons(&hIcon, &hSmIcon));
if (SUCCEEDED(hr))
{
SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hSmIcon);
}
SendMessageW(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hSmIcon);
return hr;
}
// Routine Description:
// - Initializes the icon class by loading the default icons.
// Arguments:
// - <none>
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::_Initialize()
{
auto hr = S_OK;
if (!_fInitialized)
{
#pragma warning(push)
#pragma warning(disable : 4302) // typecast warning from MAKEINTRESOURCE
_hDefaultIcon = LoadIconW(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));
#pragma warning(pop)
if (_hDefaultIcon == nullptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
if (SUCCEEDED(hr))
{
#pragma warning(push)
#pragma warning(disable : 4302) // typecast warning from MAKEINTRESOURCE
_hDefaultSmIcon = (HICON)LoadImageW(nullptr,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_SHARED);
#pragma warning(pop)
if (_hDefaultSmIcon == nullptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
if (SUCCEEDED(hr))
{
_fInitialized = true;
}
}
return hr;
}
// Routine Description:
// - Frees any non-default icon handles we may have loaded from a path on the file system
// Arguments:
// - <none>
// Return Value:
// - <none>
void Icon::_DestroyNonDefaultIcons()
{
_FreeIconFromReference(_hIcon);
_FreeIconFromReference(_hSmIcon);
}
// Routine Description:
// - Helper method to choose one of the two given references to fill the pointer with.
// - It will choose the specific icon if it is available and otherwise fall back to the default icon.
// Arguments:
// - hIconRef - reference to the specific icon handle inside this class
// - hDefaultIconRef - reference to the default icon handle inside this class
// - phIcon - pointer to receive the chosen icon handle
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::_GetAvailableIconFromReference(_In_ HICON& hIconRef, _In_ HICON& hDefaultIconRef, _Out_ HICON* const phIcon)
{
auto hr = S_OK;
// expecting hIconRef to be pointing to either the regular or small custom handles
FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));
// expecting hDefaultIconRef to be pointing to either the regular or small default handles
FAIL_FAST_IF(!(&hDefaultIconRef == &_hDefaultIcon || &hDefaultIconRef == &_hDefaultSmIcon));
if (hIconRef != nullptr)
{
*phIcon = hIconRef;
}
else
{
hr = _GetDefaultIconFromReference(hDefaultIconRef, phIcon);
}
return hr;
}
// Routine Description:
// - Helper method to initialize and retrieve a default icon.
// Arguments:
// - hIconRef - Either the small or large icon handle references within this class
// - phIcon - The pointer to fill with the icon if it is available.
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::_GetDefaultIconFromReference(_In_ HICON& hIconRef, _Out_ HICON* const phIcon)
{
// expecting hIconRef to be pointing to either the regular or small default handles
FAIL_FAST_IF(!(&hIconRef == &_hDefaultIcon || &hIconRef == &_hDefaultSmIcon));
auto hr = _Initialize();
if (SUCCEEDED(hr))
{
*phIcon = hIconRef;
}
return hr;
}
// Routine Description:
// - Helper method to set an icon handle into the given reference to an icon within this class.
// - This will appropriately call to free existing custom icons.
// Arguments:
// - hIconRef - Either the small or large icon handle references within this class
// - hNewIcon - The new icon handle to replace the reference with.
// Return Value:
// - S_OK or HRESULT failure code.
[[nodiscard]] HRESULT Icon::_SetIconFromReference(_In_ HICON& hIconRef, const HICON hNewIcon)
{
// expecting hIconRef to be pointing to either the regular or small custom handles
FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));
auto hr = S_OK;
// Only set icon if something changed
if (hNewIcon != hIconRef)
{
// If we had an existing custom icon, free it.
_FreeIconFromReference(hIconRef);
// If we were given a non-null icon, store it.
if (hNewIcon != nullptr)
{
hIconRef = hNewIcon;
}
// Otherwise, we'll default back to using the default icon. Get method will handle this.
}
return hr;
}
// Routine Description:
// - Helper method to free a specific icon reference to a specific icon within this class.
// - WARNING: Do not use with the default icons. They do not need to be released.
// Arguments:
// - hIconRef - Either the small or large specific icon handle references within this class
// Return Value:
// - None
void Icon::_FreeIconFromReference(_In_ HICON& hIconRef)
{
// expecting hIconRef to be pointing to either the regular or small custom handles
FAIL_FAST_IF(!(&hIconRef == &_hIcon || &hIconRef == &_hSmIcon));
if (hIconRef != nullptr)
{
DestroyIcon(hIconRef);
hIconRef = nullptr;
}
return S_OK;
}

View File

@ -23,33 +23,21 @@ namespace Microsoft::Console::Interactivity::Win32
static Icon& Instance();
[[nodiscard]] HRESULT GetIcons(_Out_opt_ HICON* const phIcon, _Out_opt_ HICON* const phSmIcon);
[[nodiscard]] HRESULT SetIcons(const HICON hIcon, const HICON hSmIcon);
[[nodiscard]] HRESULT LoadIconsFromPath(_In_ PCWSTR pwszIconLocation, const int nIconIndex);
[[nodiscard]] HRESULT ApplyWindowMessageWorkaround(const HWND hwnd);
protected:
Icon();
~Icon();
~Icon() = default;
Icon(const Icon&) = delete;
void operator=(const Icon&) = delete;
private:
[[nodiscard]] HRESULT _Initialize();
// We are not using unique_hicon for these, as they are loaded from our mapped executable image.
HICON _hDefaultIcon{ nullptr };
HICON _hDefaultSmIcon{ nullptr };
void _DestroyNonDefaultIcons();
// Helper methods
[[nodiscard]] HRESULT _GetAvailableIconFromReference(_In_ HICON& hIconRef, _In_ HICON& hDefaultIconRef, _Out_ HICON* const phIcon);
[[nodiscard]] HRESULT _GetDefaultIconFromReference(_In_ HICON& hIconRef, _Out_ HICON* const phIcon);
[[nodiscard]] HRESULT _SetIconFromReference(_In_ HICON& hIconRef, const HICON hNewIcon);
void _FreeIconFromReference(_In_ HICON& hIconRef);
bool _fInitialized;
HICON _hDefaultIcon;
HICON _hDefaultSmIcon;
HICON _hIcon;
HICON _hSmIcon;
wil::unique_hicon _hIcon;
wil::unique_hicon _hSmIcon;
};
}