How do I ask Windows for the size of system tray icons?
Asked Answered
H

4

10

I noticed that my app sends icons to the Windows tray with a size of 16x16 pixels--and my Vista PC I've got a doublewide taskbar that appears to show icons at 18x18. The resizing artifacts on my app's icon look awful. How can I ask Windows what size the icons should be?

Edit

I'm generating the icon dynamically, with a pixel font text overlay. It seems wasteful to generate a bunch of icon sizes dynamically, so it would be nice to avoid building an icon with all the "possible" sizes (not that I'm even sure what those are).

  • GetSystemMetrics(SM_CXSMICON) returns 16--the incorrect value.
  • GetThemeBackgroundContentRect didn't help, either.
Hypervitaminosis answered 20/2, 2009 at 3:42 Comment(0)
T
1

Create your icons in multiple formats, and let Windows select the one it wants.

Here's the Wikipedia article on the .ico format.

If you really need to know, GetSystemMetrics with a parameter of SM_CXICON or SM_CYICON will tell you the width and height that Windows wants to load.

Tomtit answered 20/2, 2009 at 3:51 Comment(1)
The first half of this answer is just wrong. The API for notification icons is Shell_NotifyIcon. That receives a NOTIFYICONDATA which contains an HICON. Now, an HICON refers to a single icon, so you have to decide the size before you call Shell_NotifyIcon. The final paragraph is accurate though.Honghonied
Z
0

Mark's core answer is the right one: Create your icons in multiple formats and let Windows choose the right one. Don't forget to do 32x32 and 64x64 icons for HighDPI scenarios.

But SM_CXICON/SM_CYICON won't necessarily return the size that will be used in the taskbar. The taskbar chooses the right icon size for it's size (this is much more important in Window 7).

Just provide appropriately sized icons and you should be ok.

Zanazander answered 20/2, 2009 at 4:7 Comment(10)
I guess I should have mentioned: I'm generating the icon dynamically, with a pixel font text overlay. It seems wasteful to generate a bunch of icon sizes dynamically. GetSystemMetrics(SM_CXSMICON) returns 16--the incorrect value again.Hypervitaminosis
Unfortunately that information doesn't change my answer - you still need to produce multiple icon sizes. Or just produce a 256x256 icon and let the system scale it down for you.Zanazander
Generating a 256x256 with pixel fonts will result in unreadable text when the icon is resampled to 16x16, unfortunately.Hypervitaminosis
A good point Kevin. As far as I know there's no way of knowing what icon size will be chosen by the shell and it absolutely does change - for example, I believe that Win7 uses 64x64 (unless you specify small icons or run in classic mode).Zanazander
@Larry For a notification area icon, how do you "let Windows choose the right one"?Honghonied
Provide multiple icons at varying resolutions. Windows knows the size of the icon it wants to display it it'll pick "the best one". The algorithm for deciding "the best one" changes from release to release - I believe that Win7 uses "If there's an exact match, use that, otherwise pick the next larger size one and scale down, otherwise pick the next smaller one and scale up". The theory is that scaling an icon down is going to look better than scaling up. But that algorithm will change from release to release (for instance, xp used a different algorithm (it didn't pick the larger one)).Zanazander
But.. How to provide it with multiple sizes? The .ico contains lots of sizes, but when you load it, you have to specify the size it'll be loaded for, and pass the HICON to the tray. An HICON has just got the icon at one size, selected when it was loaded.Southwestwards
I believe that the shell knows how to get back to the original .ico file from the HICON, but I'm not sure.Zanazander
This answer is just wrong. The API for notification icons is Shell_NotifyIcon. That receives a NOTIFYICONDATA which contains an HICON. Now, an HICON refers to a single icon, so you have to decide the size before you call Shell_NotifyIcon.Honghonied
As David says -- while a .ICO file has multiple sizes and while an ICON resource (actually an ICON_GROUP resource) has multiple sizes -- once you've called LoadIcon, you get the one closest to SM_CXICON. The documentation for NOTIFYICONDATA suggests using LoadIconMetric, which uses the algorithm that Larry describes in his comment. But there's no easy way to find out the size that it will use. I suppose you use a variety of sizes of placeholder icons in your resources, and then see which one LoadIconMetric actually chooses...Varicella
T
0

Your best bet may be GetThemeBackgroundContentRect passing TBN_BACKGROUND as iPartId for the tray notify background.

GetThemeBackgroundContentRect should return the size defined by the current theme that may be used for drawing without overlapping the borders of the parent element. If I'm reading this correctly, that would be the largest sized notification icon permissible and presumably the size that is being used.

Testing with multiple DPI settings is probably the easiest way to tell if this is returning the correct value.

Tiphani answered 20/2, 2009 at 5:26 Comment(1)
GetThemeBackgroundContentRect with TBN_BACKGROUND did not vary with the tray icon size changing, either.Hypervitaminosis
C
0

Per the NOTIFYICONDATA documentation:

If only a 16x16 pixel icon is provided, it is scaled to a larger size in a system set to a high dpi value. This can lead to an unattractive result. It is recommended that you provide both a 16x16 pixel icon and a 32x32 icon in your resource file. Use LoadIconMetric to ensure that the correct icon is loaded and scaled appropriately. See Remarks for a code example.

...

...
// Load the icon for high DPI.
LoadIconMetric(hInst, MAKEINTRESOURCE(IDI_SMALL), LIM_SMALL, &(nid.hIcon));
...

So, creating icons dynamically really isn't the best option. You should provide multiple icons of different sizes statically in your program resources and let Windows choose the best one it wants.

Clippard answered 6/2, 2023 at 19:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.