0

I have an icon file that contains a 24x24, 32x32, 48x48, 64x64, and 256x256 icon. However, when I load it like this:

Application->Icon->LoadFromFile("filename.ico");

it appears to only load the one image from the file, despite the fact that my icon contains multiple resolutions of the icon. The result is that any Forms use the single icon re-scaled for both the taskbar icon, and the corner icon, which looks bad.

However, if I set filename.ico into a compiled resource, or if I set the icon in Project Properties > Application > Icon, then my Forms use the 24x24 icon for the corner icon, and the 48x48 icon for the taskbar.

My question is: how can I have my Forms use the icons from filename.ico where the filename is not known until runtime; but still use the 24x24 icon for the corner and use the 48x48 icon for the taskbar?

NB. I prefer not to hardcode those sizes 24x24, and 48x48, because other versions of Windows (or if the person uses the windows font scaling option) may then call for a different size icon.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • using [this workaround](http://stackoverflow.com/a/18315727/1505939) in the meantime – M.M Dec 09 '14 at 22:44

1 Answers1

1

When you call TIcon.LoadFrom...(), it stores a copy of the raw icon data into an internal memory block and then exits. That block is not processed until the next time that TIcon.HandleNeeded() is called, such as when the TIcon.Handle property is used.

If the icon data represents an icon of type RC3_STOCKICON (not usually encountered), the IDI_APPLICATION icon from LoadIcon() is used. Otherwise, if the icon data represents an icon of type RC3_ICON (the usual case), the data is parsed and the image that most closely matches the current TIcon.Width and TIcon.Height property values (or the SM_CXICON and SM_CYICON metrics via GetSystemMetrics() if the TIcon dimensions have not been assigned yet) are passed to CreateIcon().

From that point on, the HICON returned by LoadIcon() or CreateIcon() is the image used for the remainder of the TIcon's lifetime, or at least until the HICON is freed/released via TIcon.ReleaseHandle(), TIcon.Assign(), TIcon.LoadFrom...(), TIcon.SetHandle(), etc.

The memory block itself is only freed when the TIcon is freed, TIcon.Assign() is called, or a new image source is loaded. So it should be possible, for instance, to call TIcon.ReleaseHandle() to release the current HICON (you will then have to free it manually via DestroyIcon()), then resize the TIcon's dimensions, and then call TIcon.HandleNeeded() to re-parse the memory block to load the next closest matching image.

Update: TIcon cannot have multiple images of different resolutions loaded at the same time. A Form's corner icon and its Taskbar icon (and remember, when Application->MainFormOnTaskbar is false, the Taskbar button is controlled by the hidden Application window, not the MainForm window, unless you override that behavior manually) are actually separate icons at the OS layer, assigned via the WM_SETICON message using different input parameters (wParam=ICON_SMALL and wParam=ICON_BIG, respectively). However, the VCL only ever uses WM_SETICON to set a window's BIG icon, never its SMALL icon. So a Form's corner icon is just a scaled down version of its Taskbar icon (when MainFormOnTaskbar is true) or the Application's Taskbar button (when MainFormOnTaskbar is false). When the VCL issues WM_SETICON for a Form, it used the Form's own Icon if assigned, otherwise it uses the Application's Icon if assigned, otherwise it uses LoadIcon() to load the default IDI_APPLICATION icon.

So, if you really want different icons of different resolutions for the Form's corner icon and Taskbar icon, you will have to use separate TIcon objects to load the desired resolution images, as described above, and then issue your own WM_SETICON messages accordingly.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • what does RC3_STOCKICON and RC3_ICON mean? – M.M Dec 10 '14 at 02:23
  • Thanks. I understand the update but the last thing I'm not clear on is how the main window gets loaded with the Small icon from the compiled-in resource (to reproduce this just make a blank project and add an icon with both big and small icons via the Project Properties > Icon). There must be code somewhere to read both of those icons and assign one to the corner. – M.M Dec 10 '14 at 03:01
  • There is no such code. As I said, the VCL only deals with the BIG icon. Study the VCL source code for yourself, you will see that to be true. As for the icon in the project properties, the `TApplication` constructor loads the `MAINICON` resource into the `TApplication.Icon` property using the `LoadIcon()` function, and thus its use is subject to the details I mentioned above. – Remy Lebeau Dec 10 '14 at 03:13
  • How do you explain that a different icon appears for the Small then? (I can post a screenshot for proof if required) – M.M Dec 10 '14 at 03:15
  • There is nothing in the VCL source code that sets the small icon, only the big icon. And an `HICON` represents a specific sized image, it cannot refer to multiple images, and an .ICO file assigned to the project properties gets split into separate resources for each image. So whatever trickery is allowing the small icon and large icon to refer to different images without the VCL assigning them explicitly has to be getting handled at the OS layer. Since you want to load them explicitly at runtime, you will have to assign them explicitly as well. – Remy Lebeau Dec 10 '14 at 03:37
  • The VCL call to CreateWindowEx passes the main form's hInstance, so perhaps CreateWindowEx reads IDI_APPLICATION if no icon was otherwise set – M.M Dec 10 '14 at 03:44
  • Maybe. The documentation for [`WNDCLASSEX`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx) states the following for the `hIconSm` member: "If this member is NULL, the system searches the icon resource specified by the hIcon member for an icon of the appropriate size to use as the small icon." The VCL uses `WNDCLASS` instead of `WNDCLASSEX`, but maybe the same search logic applies to `WNDCLASS` (since it does not have a `hIconSm` member). – Remy Lebeau Dec 10 '14 at 03:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/66532/discussion-between-remy-lebeau-and-matt-mcnabb). – Remy Lebeau Dec 10 '14 at 03:51