-2

With Delphi XE7, I want to change the icon of the current VCL form (not of the application) at runtime. So I tried this code:

procedure TForm1.LoadExeIcon(const AExeFileName: string);
var
  Icon: TIcon;
begin
  Icon := TIcon.Create;
  try
    Icon.Handle := ExtractIcon(HInstance, PWideChar(AExeFileName), 0);
    Self.Icon.Assign(Icon);
  finally
    Icon.Free;
  end;
end;

The changed icon should then be visible on the left top corner of the window (small image format) and in the taskbar (large image format).

It works, but there is a small problem: The new small icon in the top left corner of the window looks blurred, supposedly because the large image from the exe file gets stretched to the smaller size.

This is how the small image looks in the window of the original exe program:

enter image description here

And this is how the small image looks in the test program after replacement:

enter image description here

The large icon in the taskbar looks perfect.

So how can I make the small icon look nice like in the original exe file?

EDIT:

I followed David's advice and here is the working solution. To make the cake really sweet I added an overlay icon to the taskbar icon:

procedure TForm1.LoadExeIcon(const AExeFileName: string);
// Load Large and Small Icon from Exe file and assign them to the Form Icon
// Add Overlay to Taskbar Icon
var
  LIcon: HICON;
  SIcon: HICON;
  OLIcon: TIcon;
  NumberOfIconsInExeFile: Integer;
begin
  NumberOfIconsInExeFile := ExtractIconEx(PWideChar(AExeFileName), -1, LIcon, SIcon, 0);
  if NumberOfIconsInExeFile > 0 then // if there are any icons in the exe file
  begin
    ExtractIconEx(PWideChar(AExeFileName), 0, LIcon, SIcon, 1);
    SendMessage(Form1.Handle, WM_SETICON, 1, LIcon);
    SendMessage(Form1.Handle, WM_SETICON, 0, SIcon);
  end;

  // apply an overlay icon to the taskbar icon with new TTaskbar component:
  OLIcon := TIcon.Create;
  try
    ilTest.GetIcon(0, OLIcon);
    Taskbar1.OverlayIcon.Assign(OLIcon);
    Taskbar1.OverlayHint := 'My Hint'; // does not work?
    Taskbar1.ApplyOverlayChanges;
  finally
    OLIcon.Free;
  end;
end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
user1580348
  • 5,721
  • 4
  • 43
  • 105
  • Well, what happens? Doesn't work isn't helpful. What debugging did you do? What does ExtractIcon return? – David Heffernan Mar 01 '15 at 19:36
  • You are right and I am stupid. It works. My stupid error. Have been programming for the last 10 hours almost without stopping. Do you agree I delete the question? – user1580348 Mar 01 '15 at 19:41
  • Have rephrased the question. – user1580348 Mar 01 '15 at 19:51
  • No. Now you are asking a different question. – David Heffernan Mar 01 '15 at 19:54
  • OK. I am asking a different question. – user1580348 Mar 01 '15 at 19:57
  • That's not how to do it. Someone with your experience here really should know that by now. – David Heffernan Mar 01 '15 at 19:58
  • So how to do it? Delete this question and ask a new one? – user1580348 Mar 01 '15 at 20:02
  • That's how I would have done it. Of course that would delete my answer too now. – David Heffernan Mar 01 '15 at 20:10
  • @user1580348 Don't forget to close icon handles once you no longer need them by using CloseHandle(LIcon) and CloseHandle(SIcon); – SilverWarior Mar 01 '15 at 20:45
  • Thanks SilverWarior, I've edited the source in the above solution. – user1580348 Mar 01 '15 at 21:14
  • Lots of errors in that code. No error checking for a start. CloseHandle is wrong. It's `DestroyIcon`. The says so quite clearly. Don't write win32 code without careful reading of docs. – David Heffernan Mar 01 '15 at 21:20
  • @Silver That's wrong. Read the docs one more time. CloseHandle is for kernel32 objects, not for user objects. Here it's `DestroyIcon`. – David Heffernan Mar 01 '15 at 21:21
  • If I use `DestroyIcon` instead of `CloseHandle` (`DestroyIcon(LIcon); DestroyIcon(SIcon);`), then the whole thing doesn't work. Try it out yourself. – user1580348 Mar 01 '15 at 21:34
  • If instead of playing this trial-and-error game ad infinitum you would post your version of the solution the whole thing would be easier... – user1580348 Mar 01 '15 at 21:37
  • Ok. You must not destroy the icon. The form now owns it. I don't want to write the code for you. It would be better for you to learn to do it yourself. – David Heffernan Mar 01 '15 at 21:43
  • OK, now I've added a check at the beginning whether there are are actually any icons in the exe file. For me it works perfectly. – user1580348 Mar 01 '15 at 22:25
  • Why are you calling CloseHandle and passing an icon? We just did this. Destroy icons with `DestroyIcon`. Only here you don't own them, the window does, so don't destroy them. The window will. – David Heffernan Mar 01 '15 at 22:26
  • I edited your code to hopefully make it clear. – David Heffernan Mar 01 '15 at 22:30
  • This seems to have overlapped: I edited it at the same time as you. Could you re-edit it please? – user1580348 Mar 01 '15 at 22:34
  • Done. Silver and I confused you. Silver with the bum steer re CloseHandle. And me for not recognising that you'd passed on the ownership. – David Heffernan Mar 01 '15 at 22:39
  • All is well that ends well. A little bit of confusion in the evening adds more suspense than watching a boring tv show... – user1580348 Mar 01 '15 at 22:44
  • @DavidHeffernan Ups I did it again. I read the docs to quickly. – SilverWarior Mar 01 '15 at 22:54
  • Strange: It seems the OVERLAY thing works only for main forms. I tried it on a secondary form and it didn't work. Does anybody know more? – user1580348 Mar 01 '15 at 23:56
  • @user you've asked multiple questions now. What about the question you asked? Can't we finish that? If you want to ask a new question, that's a new question. – David Heffernan Mar 02 '15 at 06:46
  • You are meant to ask a single question. You are on to your third now. It's meant to be one Q so that we have a clear target to aim our answers at. My answer addresses your second Q. Which is already dubious. We should, technically, have moved the second Q into a new question. Your other answer addresses the first Q. I don't feel too bad about that answer because it was a wrong answer to Q1. But I hope you see my point. One question at a time is needed. Thanks. – David Heffernan Mar 02 '15 at 07:43
  • Technically, it was not a QUESTION, it was a COMMENT. – user1580348 Mar 02 '15 at 08:06
  • OK, but you were asking a new question in a comment. I'm sure we can help, but that would really need a new question. I just couldn't see where this was going to end. – David Heffernan Mar 02 '15 at 11:10
  • David, you are right that I COULD ask a new question. However, I believe you make several wrong assumptions about what you initially said was a "new question". The initial change in my question text was a SUBSET of the initial question. Following the rules of logic and set theory, a subset of a subject is NOT a change of the subject but rather a process of focusing on specific parts of the subject. A change of the subject would be the lack of an essential intersection between two subjects. So please be more rational when assessing user questions. – user1580348 Mar 02 '15 at 12:43

2 Answers2

1

Call ExtractIconEx to extract both large and small icons.

However, do be warned that the VCL has a design flaw, introduced in Delphi 1, and does not allow you to set both small and large icons. You are, in my view, better off ignoring the Icon property and sending WM_SETICON manually. Once for each icon size.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Would `Perform` be a better choice? – user1580348 Mar 01 '15 at 20:39
  • Better choice than what, for what? – David Heffernan Mar 01 '15 at 20:41
  • Instead of `SendMessage`. – user1580348 Mar 01 '15 at 21:10
  • I'd call SendMessage. – David Heffernan Mar 01 '15 at 21:12
  • Not even sure Perform will work. But why be indirect. – David Heffernan Mar 01 '15 at 21:42
  • I accepted this answer although I self provided the concrete solution in the question text. This is because: 1. You gave a correct initial advice in the right direction 2. you spent a considerable amount of your valuable time discussing in the comment section, although a certain part of the discussion was dedicated to the general subject of question changes. – user1580348 Mar 02 '15 at 12:52
  • We don't always provide all of the code in answers. At some point, we have to stop writing your code, and leave it to you. We can debate where that point might be. And it's different for different people. It's clear that you are quite competent enough to write the code yourself. So I feel it was fine to include high level advice. – David Heffernan Mar 02 '15 at 13:05
-1

Try remove Icon.Free; , if it works it means that Icon.Free; also invalidate the assigned Icon Handle

Luca Rocchi
  • 6,272
  • 1
  • 23
  • 23