How to accomplish the following goal in Delphi 10.2 Tokyo: I need Delphi to automatically set not just the big icon, but both the big and small icons for each window. I need to have an opportunity, for some of the forms, and for the TApplication, to change icons at run-time. I want this to be accomplished without modifying VCL.Forms.pas
(the small icon is one that displayed in a window title bar, to the left from window caption).
There is a function in TCustomForm
:
function GetIconHandle: HICON;
Unfortunately, Delphi only sets big icon handle, for example, here is a quote from VCL.Forms.pas
:
SendMessage(Handle, WM_SETICON, ICON_BIG, GetIconHandle);
As you see, the above code only sets the big icon handle, but I also need to set the small icon handle, since the .ICO files that I use contain different images for big and small icons.
Let me brefly summarize the differences between the big and the small icons, since even the Microsoft documentation almost tells nothing about it. Here are the main differences:
Small icon image is displayed on the window title bar.
Large icon image is displayed in the Windows taskbar (usually located at the bottom part of the screen), if the taskbar is thick; the large icon image is also displayed when you press Alt+Tab.
See https://blog.barthe.ph/2009/07/17/wmseticon/ for more information on big and small icons.
Delphi, by setting only the large window handle, effectively phases out the alternative image for a smaller icon shown on window titles. If only big icon is given but not the small one, Windows resamples the image from the bigger icon to the smaller one, quality worsens, and the main idea of a smaller, simpler image is lost.
See the example image courtesy sanyok. The left column, labeled v7.4.16 are screenshots from a program compiled with a code that sets both ICON_BIG
and ICON_SMALL
. The right column, labeled v7.4.16.22 is a screenshot from the same program that doesn't explicitly sets both small and big icons, but just assigns TIcon
to a form, an then Delphi using its standard code just assigns just the big icon, so the image from the windows title bar is resized by Windows from the large icon. You may see how poor the quality becomes as a result of standard Delphi behaviour.
In the past, I was changing the GetIconHandle in the interface section of the VCL.Forms.pas
from static to virtual, changing it from function
to procedure
and adding two parameters:
procedure GetIconHandle(var Big, Small: HICON); virtual;
So the subsequent code in the VCL.Forms.pas was like the following:
var
Big, Small: HICON;
begin
[...]
GetIconHandle(Big, Small);
SendMessage(Handle, WM_SETICON, ICON_BIG, LParam(Big));
SendMessage(Handle, WM_SETICON, ICON_SMALL, LParam(Small));
[...]
Is it possible to easily accomplish this without modifying the VCL.Forms.pas
?
I did solve the problem in Delphi 2007 by modifying VCL units, but I can no longer modify VCL units in Delphi 10.20 Tokyo for the following reasons:
VCL units compile, but then, when I compile my application, I get "Internal Error: AV0047C6C7-R000004CC-0", regardless of a target targets (Win32/Win64; Debug/Release), see https://quality.embarcadero.com/browse/RSP-18455 - the first part of the error number (address) is different, but the second - R000004CC-0 - is always the same.
I have to manually add (TObject) to each of the classes that do not inherit from any class; otherwise I gen an error that
Create
orDestroy
is not found in base class. In previous versions of Delphi, writing simplyclass
without any ancestor implicitly inherited it fromTObject
, but when I compile code from command-line bydcc32
withdcc32 -Q -M -$D- -$M+
command line options, this error occurs thatCreate
orDestroy
is not found in base class.
Here is how I did loaded the icons in the past:
procedure LoadIconPair(var Big, Small: hIcon; AName: PChar);
begin
if Win32MajorVersion < 4 then
begin
Big := LoadIcon(hInstance, AName);
Small := 0;
end
else
begin
Big := LoadImage(hInstance, AName, IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
Small := LoadImage(hInstance, AName, IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
end;
end;
This code can be further improved: the hard-coded sizes of 32x32 and 16x16 can be changed, as suggested at https://blog.barthe.ph/2009/07/17/wmseticon/ , to
GetSystemMetrics(SM_CXICON)
, GetSystemMetrics(SM_CYICON)
for big icons and GetSystemMetrics(SM_CXSMICON)
and GetSystemMetrics(SM_CYSMICON)
for small icons.
So, each form essentially called LoadIconPair
and then returned the handles via the overwritten procedure GetIconHandle(var Big, Small: HICON); override;
.
So the questions are the following:
Is it possible to let Delphi set both small and big icons without much hassle and without modifying
VCL.Forms.pas?
(this is the main question) -- I need to have an opportunity, for some of the forms, and for the TApplication, to change icons at run-time.;If not, how to add a modified source VCL unit to your application under Delphi 10.2 Tokyo, where the interface section of a unit is modified? Are there any instructions, or an official guide? If anybody managed to do that, how did you accomplish that? Did you compile them from the GUI IDE? Or using command line dcc32/dcc64? Or using msbuild? Or otherwise? Do you also have to manually add (TObject) to classes that do not inherit from any class to avoid the
Create
orDestroy
is not found in base class error?
Update #1: Setting the icons again after VCL.Forms.pas has set it is not a complete solution: we have to also take care about the Application icon, not only the forms icons; besides that, VCL.Forms.pas sets the icons anyway, but only the ICON_BIG
, we have to set the icons again, this time setting both small and big. Do you have an idea on how can we patch the VCL.Forms.pas to add setting the ICON_SMALL
whenever it sets the big icon, so we only patch the implementation
section, and will call some messages, even WM_USER+N to request the icon handles from the form, and our descendant of TForm will implement this message handler?
Update #2: TApplication and TForm have similar interfaces with regard to icons, but TApplication is a descendant of TComponent which do not have a window handle, and, respectively, don't have message handlers. What we can do with a TForm, we cannot do with TApplication.
Update #3: I have implemented a solution which is a mixture of what kobik has suggested in his post and Sertac Akyuz has suggested in his later post. Thank you also to the other people who have contributed in the comments. I've compiled the program and gave it to the beta testers and they have confirmed that the problem has been fixed, the icon look good now, also the animation of the icon in the TApplication via changing icons by timer is also working properly. Thank you all!
GetIconHandle
method looks like? What kind of icons you use? I mean is it consists of multiple sizes? (Can you upload a sample?) Also do you override this method for each form and return the appropriate icons? To be honest I never even thought about this issue... Interesting. in any case patching theinterface
section is a bad idea IMO. – kobikUpdate #2: TApplication and TForm have similar interfaces with regard to icons, but TApplication is a descendant of TComponent which do not have a window handle...
this is simply wrong.Application.Handle
represent its internal Window handle. You canSendMessage(Application.Handle, WM_SETICON...
as I mentioned in my answer. maybe that was something you missed all along, – kobikso you cannot unify the work with TForm and TApplicatoin via a common ancestor
- This is true. I never told otherwise. but I explained how to handle the Application icon. – kobik