How to use WM_SETICON Properly
How NOT to use WM_SETICON
It’s not rocket science, but you’d be surprised to see how many people get this wrong. Here’s a code snippet of what people usually do:
const HICON hicon = ::LoadIcon(
::GetModuleHandle(0),
MAKEINTRESOURCE(IDR_MAINFRAME)
);
if (hicon)
{
::SendMessage(hwnd, WM_SETICON, ICON_BIG, reinterpret_cast(hicon));
::SendMessage(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast(hicon));
}
The code above would set the large icon (as displayed in the ALT-TAB switch window dialog) and the small icon (as displayed in the window title bar or the task bar) of the window (whose handle is hwnd
).
Except for the error checking which is not particularly thorough, most people would consider this code to be correct. You could also have used LoadImage
with LR_DEFAULTSIZE
.
Why this is wrong
The problem reveals itself with an icon which has a slightly different design for the small 16×16 size compared to the large 32×32 size. The 16×16 icon will look like a scaled down version of the 32×32 one. The reason why the smaller icon looks different in the first place is usually because the large icon’s design is a bit overloaded and cannot be downscaled without becoming unrecognizable; so you will notice!
What’s wrong? If you dig into the documentation you will found the following information:
LoadIcon
loads the most appropriate icon, at one fixed size, which isSM_CXICON
bySM_CYICON
pixels, i.e. the size of a large icon.- The small icon should be
SM_CXSMICON
bySM_CYSMICON
pixels. WM_SETICON
forICON_SMALL
would downscale a larger icon toSM_CXSMICON
bySM_CYSMICON
pixels automatically.- This odd behavior is due to backward compatibility with legacy versions of Windows, which only had one size of icons.
How to use WM_SETICON
Now that we understand the problem (the wrong size of the icon is loaded), we know how to fix it. The solution is to call LoadImage
twice and explicitly provides the size of the icon to avoid the downscaling.
const HANDLE hbicon = ::LoadImage(
::GetModuleHandle(0),
MAKEINTRESOURCE(IDR_MAINFRAME),
IMAGE_ICON,
::GetSystemMetrics(SM_CXICON),
::GetSystemMetrics(SM_CYICON),
0);
if (hbicon)
::SendMessage(hwnd, WM_SETICON, ICON_BIG, reinterpret_cast(hbicon));
const HANDLE hsicon = ::LoadImage(
::GetModuleHandle(0),
MAKEINTRESOURCE(IDR_MAINFRAME),
IMAGE_ICON,
::GetSystemMetrics(SM_CXSMICON),
::GetSystemMetrics(SM_CYSMICON),
0);
if (hsicon)
::SendMessage(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast(hsicon));
References
Links to MSDN: