Change icon

kapot
kapot used Ask the Experts™
on
Hi,

Do you know any way how to change .exe icons using Delphi ?

I tried "madRes" from madshi.net, but it can only change the "main icon" (icon that is displayed by Windows Explorer). At least I use the example from madshi website.

I need to change icons properly, the one that is displayed by Windows Explorer, the one that is displayed in the taskbar, the one in the top left Delphi TForm ...

Please if you know how to do that.

Thanks.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2007

Commented:
all the icons you enumerated are 1 icon: the main icon. but that icon is a multi-page icon. so it's one ico file that contains a few icons of different sizes which are autoamtically picked by windows when needed: in a small area, like taskbar, a small icon is choosen. in a big era like when viewing icons in explorer, a bigger icon is choosen.
in case there are any other icons in the exe, you can also change them too using madres, but instead of providing MAINICON, you will need to provide the correct group. in order ot know the correct group, you need a resource editor to see the group. for example xn resource editor http://www.wilsonc.demon.co.uk/d10resourceeditor.htm

Author

Commented:
I checked using ResHack and I saw another group of icon "128" besides "MAINICON".

I changed the group "128", but the result is ... madReas added a new "128" group instead of changing it.

Then I checked again using ResHack, the correct "128" group has "1033" sub-group.

The added "128" has "0" sub-group.

Still cant figure out using madRes.

Author

Commented:
Here is the function that I use to change EXE icon :

function UpdateExeIcon(exeFile, iconGroup, icoFile: string) : boolean;
var resUpdateHandle : dword;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then begin
    result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), 0, PWideChar(wideString(icoFile)));
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;
Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

Author

Commented:
I also tried to add that "language" parameter :

function UpdateExeIcon(exeFile, iconGroup, icoFile: string; language: word) : boolean;
var resUpdateHandle : dword;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then begin
    result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, PWideChar(wideString(icoFile)));
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;

But that does not help. It created a duplicate entry of the same icon group !
Top Expert 2007

Commented:
well .. then you are not interpreting the data correctly.

here is my test (using xn resource editor)
- exe file with 3 icons:
-- MAINICON (language is my local language)
-- ICON1 (language neutral)
-- ICON2 (language neutral)

function UpdateExeIcon(exeFile, iconGroup, icoFile: string; language: word) : boolean;
var resUpdateHandle : dword;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then begin
    result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, PWideChar(wideString(icoFile)));
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  UpdateExeIcon('c:\test.exe', 'ICON1', 'c:\test.ico', 0);
end;

the icon is beeing correctly replaced.
so, I am guessing that your non-main icons are not language neutral and since you are using 0, the new icon is added.

btw, I'm using
madRes.pas                version:  1.0j  ·  date: 2005-07-15

Author

Commented:
In my case,

The MAINICON is using language = neutral

and the other icon group is "128" using language = English US.

I tried to change that "128" icon group using language = 1033

And it does not work.

Maybe if we can find a way to LIST all the icon groups and the language?
Top Expert 2007

Commented:
well, that depends a little on what you are doing.
so before getting into more details, lets start with the begining:
- what excatly are you trying to do?
- how do you know which icon you want to modify?
Are you looking to change the icon of your exe ? Then go to
Project > Options > Application Tab > Click  'Load Icon' button
You can choose the ico extension icons which will set for the exe icon as well as for the forms.

Author

Commented:
I want to change the icon of an EXE file during runtime.

I meant something like this: I have a main application called PUBLISHER.EXE which can be used to change any EXE file icon.

So the EXE file that I want to change is different EXE file, not the PUBLISHER.EXE.

Using the madRes unit, I can only change the MAINICON Icon Group, not other icons.

That's my current problem.
Top Expert 2007

Commented:
kapot, still one question unanswered:
- how do you know which icon you want to modify?

Author

Commented:
Ciuly,

At this moment, I use ResHack or Wilson Resource Editor to "see" which icon group that need to be changed.

Then I tried the madRes unit to change that, which it failed.

I can see MAINICON with language code = 0, also "128" with language code = 1033.

But as I mentioned, madRes could not change the one with language code 1033. It will create duplicate, same "128" icon group and same language code 1033.

It would be nice if we can enumerate (make a list) of available icon group and its language code and then change it programatically.

I am thinking there is a bug in the madRes unit so it cannot REPLACE icon group other than MAINICON, or it cannot replace if the language code is not 0.
Top Expert 2007

Commented:
>>I am thinking there is a bug in the madRes unit so it cannot REPLACE icon group other than MAINICON,
as I told you, this worked for me. so this is out of the question

>> or it cannot replace if the language code is not 0.
this is false as well.

one thread worth reading: http://forum.madshi.net/viewtopic.php?t=273

I've made some tests and found that the problem is the resource group name beeing "128". if you rename that to anything else (non-numeric), than you can change the icon without problems.

I'm using the following code:

program Project2;

uses
  windows, madres;

{$R *.res}

function MAKELANGID(sPrimaryLanguage : Word;
                    sSubLanguage : Word) : Word;
begin
  result := (sSubLanguage shl 10) or
             sPrimaryLanguage;
end;

function UpdateExeIcon(exeFile, iconGroup, icoFile: string; language: word) : boolean;
var resUpdateHandle : dword;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then begin
    result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, PWideChar(wideString(icoFile)));
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;

begin
  UpdateExeIcon('test.exe', 'TEST', 'red.ico', makelangid(LANG_ENGLISH,SUBLANG_ENGLISH_US));
end.

created a test application:

program Project1;

{$R *.res}
{$R icons.res}

begin
end.

and the icon resource:icons.rc
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
128 ICON "green.ico"
TEST ICON "green.ico"

compiled with brcc32 icons.rc resulting in icons.res

then compiled project1 and renamed to test.exe and then run project2 and the test icon changed from green to red as expected. language stood the same. no new groups added
if I change 'TEST' to '128' in project 2, then this is no longer true.

I will post on madshis forum and see what he has to say about this.

an alternative would be to use the xn resource components/classes
ZhaawZSoftware Developer

Commented:
>> I need to change icons properly, the one that is displayed by Windows Explorer, the one that is displayed in the taskbar, the one in the top left Delphi TForm ...

Those icons don't have to be the same actually. iirc, the first icon in resources is the one that is visible in Explorer (it can be multipage icon for multiple icon sizes / colors). Other icons (in taskbar, in form's corner, in alt+tab window) may be whatever that exact application loads as window's icon. For "standard windows" those 3 icons are the same icon (which can be multipage icon for displaying "different" icons when different sizes are needed), which is loaded before calling RegisterClass() or RegisterClassEx(). With "standard window" I mean a window that is visibile in taskbar and as a window on desktop. In Delphi applications you actually don't see forms (TForm*) in taskbar; instead you see TApplication which may have different icon than forms (you don't see TApplication as a window, you see it only in taskbar; you don't see forms in taskbar, you see only windows on desktop).

If there is one exact program you want to play with, you can, of course, try to figure out, which icon is where, but I think there is not a universal way for changing icons in all applications (for .exe file - there is; for forms' corner / taskbar - there is not).
developmentguruPresident

Commented:
I am coming into this conversation late, so pardon me if I ask any redundant questions.  I have the sneaking suspicion that the icon you are trying to change may be accessed by text 128 (as opposed to numeric 128).  It is also possible that the icon text 128 may be something like '128 ' (with a space).  It would help us all greatly if we had access to the EXE in question...

Let me know.
Top Expert 2007

Commented:
you can generate it yourself with the info I've given above. you have the rc file there as well, you can create the icons in whatever you like (or just grab 2 from somewhere).
I just tried using #128, ' 128', '128 ' but same behaviour.
I looked in the binary file and the data is stored like this:
0B 00 P 00 A 00 C 00 K 00 A 00 G 00 E 00 I 00 N 00 F 00 O 00  (PACKAGEINFO)
08 00 M 00 A 00 I 00 N 00 I 00 C 00 O 00 N 00 (MAINICON)
04 00 T 00 E 00 S 00 T 00 (TEST)
00 00 (I would have expected this to be 128, but it's not)
28 00 00 20 00 blablabla

anyway, I made a small trace through madres and it seems that it just doesn't load that icon at all.
I'll do some more research shortly.
Top Expert 2007

Commented:
ok. found the problem and fixed it :)

correct function is here:

program Project2;

uses
  windows,
  madres,
  sysutils;

{$R *.res}

function MAKELANGID(sPrimaryLanguage : Word;
                    sSubLanguage : Word) : Word;
begin
  result := (sSubLanguage shl 10) or
             sPrimaryLanguage;
end;

function UpdateExeIcon(exeFile, iconGroup, icoFile: string; language: word) : boolean;
var resUpdateHandle : dword;
    c:TPIconGroup;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then
  begin
    if GetIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, c) then
      result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, PWideChar(wideString(icoFile)))
                                                                                             else
    if StrToIntDef(iconGroup, -1)>-1 then
      result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(pointer(strtoint(iconGroup))), language, PWideChar(wideString(icoFile)))
    else
      result := false;
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;

begin
  UpdateExeIcon('test.exe', '128', 'red.ico', makelangid(LANG_ENGLISH,SUBLANG_ENGLISH_US));
end.

Author

Commented:
Ciuly ! You are genius! :)

However, there is one problem: It seems, I cannot change the 'MAINICON' like this :

UpdateExeIcon('test.exe', 'MAINICON', 'red.ico', 0);

Could that be because of the language = 0 ? I need that 0 btw.

Author

Commented:
Btw, could you also explain to me in a very short sentence ... what was the problem with the original function? You said that you found the problem and fixed it :)
Top Expert 2007

Commented:
the problem was that the 128 was saved as a numebr, not a text. so you needed to pass it the number (see the string to integer to pointer to pwidechar conversion there? that's how the number is passed)

I'll have to look into the mainicon thingie.
Top Expert 2007
Commented:
got it. here is the final function:

function UpdateExeIcon(exeFile, iconGroup, icoFile: string; language: word) : boolean;
var resUpdateHandle : dword;
    c:TPIconGroup;
begin
  resUpdateHandle := BeginUpdateResourceW(PWideChar(wideString(exeFile)), false);
  if resUpdateHandle <> 0 then
  begin
    if (icongroup='MAINICON') or GetIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, c) then
      result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(wideString(iconGroup)), language, PWideChar(wideString(icoFile)))
                                                                                             else
    if StrToIntDef(iconGroup, -1)>-1 then
      result := LoadIconGroupResourceW(resUpdateHandle, PWideChar(pointer(strtoint(iconGroup))), language, PWideChar(wideString(icoFile)))
    else
      result := false;
    result := EndUpdateResourceW(resUpdateHandle, false) and result;
  end else
    result := false;
end;

use correct language and it will work. I haven't debugged this to see why GetIconGroupResourceW is not seeing the MAINICON, I just had a hunch and went for it :)

Author

Commented:
Thanks ciuly, you are the best !

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial