How do I override the default icon selection algorithm?


The usual way of loading an icon from a resource is to use the Load­Icon or Load­Image function and letting the window manager's icon selection algorithm decide which image to use based on the images available in the icon, the desired icon size, and the current color depth. A customer wanted to override that algorithm, because the window manager uses the current display color depth to select an image, but they were obtaining the icon for printing purposes, so they wanted to get the highest-color-quality icon rather than the one that matched the screen's color depth. How do you override the default algorithm?

You basically do the same thing the window manager does. As we saw earlier, icon resources are actually stored in multiple pieces. The thing you use to talk about icons is actually the icon directory, which in turn points to a set of images. The first step, then, is to obtain the icon directory.

HRSRC hrsrcIcon = FindResource(hResources,
                     MAKEINTRESOURCE(IDI_MY_ICON), RT_GROUP_ICON);
HGLOBAL hIcon = LoadResource(hResources, hrsrcIcon);
auto lpIcon = static_cast<GRPICONDIR *>(LockResource(hIcon));

You then take the images listed in the GRPICONDIR and apply your custom algorithm to decide which one you like best. (If you want to use the default algorithm, you can call Lookup­Icon­Id­From­Directory or Lookup­Icon­Id­From­Directory­Ex. But if you want to use the default algorithm, then just use Load­Image already!)

When you've found the image you like, take the nId, and that's the resource ID for the RT_ICON.

HRSRC hrsrcImage = FindResource(hResources,
                     MAKEINTRESOURCE(nId), RT_ICON);
HGLOBAL hImage = LoadResource(hResources, hrsrcImage);
auto lpImage = static_cast<PBYTE>(LockResource(hImage));

You can then convert the icon image data into an icon by using the Create­Icon­From­Resource or Create­Icon­From­Resource­Ex function.

Comments (13)
  1. Andrew says:

    "If you want to use the default algorithm, you can call Lookup­Icon­Id­From­Directory or Lookup­Icon­Id­From­Directory­Ex. But if you want to use the default algorithm, then just use Load­Image already!"

    This seems to be erroneous. Both methods use the default algorithm?

  2. SimonRev says:

    The Ex version lets you specify a desired size.  But both use the default algorithm for color depth selection.

  3. @Andrew: he's saying don't waste time if you want the default behavior of the current color depth – just use LoadImage.  But if you need to do something different, then carry on…

    In other words, if your custom algorithm == default algorithm, just use LoadImage.

  4. 640k says:

    Load­Icon should have loaded all icons, and DrawIcon/DrawIconEx should have decided which resolution to use. The current architectural choices makes me cringe!

    ["Why does my 16×16 icon take up a quarter megabyte of memory?" -Raymond]
  5. Klimax says:

    Just a reminder that LoadIcon and co are from time, where RAM saving had high prioirty.

  6. Clipboarder Gadget says:

    Sadly it seems that the LoadIcon algorithm is broken since Vista. On a 150% scale DPI setting it loads always the lower resolution and never the higher. Because of that almost all application icons look blurry on the taskbar if you disable "Always combine" for the taskbar. Only some MS applications dont seem to do that, but only because they provide icons in all kinds of sizes.

  7. 640k says:

    If a 0.25mb ico file is compiled into the binary, there's of course no surprise that it will use 0.25 of ram when loading it.

    [It's a surprise if you aren't the one who compiled the binary. LoadImage("Notepad.exe", 16, 16) suddenly requires 25x more memory than it used to. A folder in small icon mode would not be able to show more than 4000 files in a directory before running out of memory *just for the icons*. -Raymond]
  8. @640K: If you always load *all* versions of an icon, there's of course no surprise that it uses lots more ram than if you only load what was actually needed.

  9. 640k says:

    [It's a surprise if you aren't the one who compiled the binary. LoadImage("Notepad.exe", 16, 16)…]

    Load­Icon isn't LoadImage. If you don't specify a size (i.e. Load­Icon), then it's a surprise that the whole icon, including all it's resolutions, isn't loaded.

    [Load­Icon loads the icon at standard size (typically 32×32). I can't wait for Alex to say "designed without adult supervision." -Raymond]
  10. Giulia Q says:

    Shouldn't pointers to resource data be decorated with const and __unaligned?

    [Resource data is 4-byte aligned, so you don't need to say __unaligned unless you have data that requires stricter alignment. You can say const if you want. That wasn't really the point of the article. -Raymond]
  11. @Clipboarder Gadget:  Maybe the offending apps/icons are not DPI-aware?  If the app hasn't declared itself DPI-aware in the manifest, Windows Vista/7 will emulate 96 DPI which might explain your behavior.

  12. Clipboarder Gadget says:

    No the programs are DPI-aware.

    Even if you take a simple program from Petzolds samples with an icon and make it DPI-aware, Windows will take the 16-sized icon and scale it up even though there is a 32-sized icon.

    You can prevent this by calling LoadIconWithScaleDown or ExtractIconEx instead of LoadIcon but most programs don't seem to do that.

Comments are closed.