3

I've been trying to change the icon of an exe programmatically, on Windows. I have a .exe file, and a .ico file, and I want the icon of the .exe to become the .ico file.

I've seen this thread and I'm not too sure about if it is applicable here. That user knew the following: 'the icon the exe uses in the app's .rc file is IDR_MAINFRAME (ID 128)'. I'm not sure what IDR_MAINFRAME is exactly or if it's useful in my case (furthermore, I can't even find what #include is needed for it).

My current code is the following:

HANDLE exe = BeginUpdateResourceW(exe_path, FALSE);

UpdateResourceW(exe, 
                RT_GROUP_ICON, 
                RT_GROUP_ICON,    // I think this might be the issue?
                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), 
                icon,             // a .ico file, in memory, as raw binary data
                icon_size);

EndUpdateResource(exe, FALSE);

CloseHandle(exe);

I've omitted error-handling code but I am checking the return values of all Windows API calls and they return successfully. I also notice that the .exe in question is updated in some way: it gets a new 'date modified' value, and OneDrive (which I have running in that directory) recognizes the file as modified because it prompts it to upload to the cloud. However, the icon doesn't change, even after restarting Explorer and even when explicitly deleting the icon cache.

I think the issue may be the lpName parameter of UpdateResourceW, but I'm not sure exactly what value is meant to go there. As I said above, the previous post had MAKEINTRESOURCE(IDR_MAINFRAME) but I'm not sure if this is appropriate or how to get IDR_MAINFRAME.

alex
  • 61
  • 5
  • Have you figured out what the ID is of each of the resources that are in your executable? Maybe use something like [Resource Hacker](http://www.angusj.com/resourcehacker/)? I don't think you will get too far if you are not able to identify the ID of the resource you are updating, so you need to use a resource viewer / editor or if it is your application, look at the RC file and determine what the ID's are. – PaulMcKenzie May 12 '23 at 13:01
  • Yes, use MAKEINTRESOURCE(). But not the only problem, surely how to got to post the wrong approach, Explorer caches icons in order to speed up its window updates. Google "reset shell icon cache" to get ahead. – Hans Passant May 12 '23 at 14:35
  • Icons have a bit of a history, and icon resources are the apparent artifacts. Updating an icon resource requires that you update the `RT_GROUP_ICON` resource (a directory of images), as well as an `RT_ICON` (the image data) resource for each referenced image. This is tedious already, but the binary layout of .ico files and icon resources is also slightly different, so you cannot just dump the individual portions of the .ico file either. Instead, you'll have to write (partial) .ico parser, too, just to get going. – IInspectable May 12 '23 at 18:12
  • Helpful resources: [The format of icon resources](https://devblogs.microsoft.com/oldnewthing/20120720-00/?p=7083), and [The evolution of the ICO file format, part 1: Monochrome beginnings](https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513) (and the rest of the series). And yes, the resource ID in the call to `UpdateResourceW` needs to adjusted to whatever the application assumes. – IInspectable May 12 '23 at 18:13
  • @PaulMcKenzie That's a good suggestion, thank you for linking that program. – alex May 13 '23 at 21:15
  • @IInspectable Thanks for the info. Do you happen to know if there are Windows API calls that are capable of doing what I'm hoping? I.e. loading in .ico files in the right format for updating a resource? [This thread](https://stackoverflow.com/questions/54718086/how-to-set-the-icon-of-an-application-exe-file-programmatically) shows how to load .ico files to change icons in some capacity (this is a different kind of icon from what I want so I understand it's not the same, but it seems conceptually similar). I would be surprised if there were no way to do this via API calls... – alex May 13 '23 at 21:19
  • I'd be surprised if there was an API call for this translation. It's only ever needed when preparing icon resources to be linked into an executable image, and [rc.exe](https://learn.microsoft.com/en-us/windows/win32/menurc/resource-compiler) is the tool that does that. You will have to read the .ico file and construct the `GRPICONDIR` in memory, making sure to replace the `dwImageOffset` with the desired `nId`. The individual images can be written as-is into `RT_ICON` resources. Make sure to assign IDs that aren't used for other `RT_ICON` resources. – IInspectable May 14 '23 at 13:41
  • Note that there are API calls for the opposite direction, e.g. [`CreateIconFromResource`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconfromresource). – IInspectable May 14 '23 at 13:42
  • @IInspectable I see. I understand that .ico files can support png data within them (as well as bitmap data). Do you happen to know if this would require special treatment in parsing? I'm not exactly sure how png data interacts with the icon resource format... (i.e. can it be dumped into the file in the same way as bitmap data, assuming correct parsing?) – alex May 14 '23 at 14:01
  • I would think that PNG icons don't need any special treatment, though I haven't tried. Even though they are [encoded](https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473) in a weird way, dumping the data identified by the `dwImageOffset`/`dwBytesInRes` pair should just work. I'll see if I can put together an answer, seeing that this is a somewhat complex task. If you happen to have a PNG .ico at hand, could you add it to the question (e.g. as [base64-encoded](https://www.base64encode.org/) text). – IInspectable May 14 '23 at 14:22
  • @IInspectable I wasn't able to get base64 encoding to work (for some reason, the decoded text didn't give back the file), but I've uploaded the file [here](https://file.io/7oeirpO1UEwM). Opening the file in a text editor shows `PNG` which I assume means that the process I used to create this imaged worked. – alex May 14 '23 at 21:41
  • @IInspectable Regarding PNG .ico files: I have managed to get a .ico parser working, and it does indeed work to just dump the binary image data into the appropriate place (an RT_ICON resource), without special handling. – alex May 16 '23 at 20:21

2 Answers2

1

You are right, the problem is in the lpName parameter. (There may be other problems too, but this is a problem for sure.)

For lpName (3rd parameter) instead of RT_GROUP_ICON you need to pass MAKEINTRESOURCE(128).

IDR_MAINFRAME is probably the name by which the icon is referred to in the source code from which the executable was created, so it is completely irrelevant to you. However, the documentation you found states that its value is 128, so that's all you care about.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • It seems like I didn't explain myself clearly enough, but that was just some example - it wasn't the executable I was trying to modify. I've written up what seems to be the solution in the answer (just putting this here for potential future readers). – alex May 15 '23 at 14:24
  • @alex ok, I am glad you found a solution. – Mike Nakis May 15 '23 at 15:59
1

I think that I have gathered enough information for this question to be answered.

The main 2 issues are:

  1. Updating the 'wrong' resource
  2. Using wrongly formatted binary data to update the resource

In regards to (1), it seems that:

  • .exe files use the first icon resource (RT_GROUP_ICON) that they can find, which seems to be the one with the lexicographically 'smallest' name
  • some common 'names' for the main icon are a numerical ID of 1 (MAKEINTRESOURCE(1)) or the string 'MAINICON', but in general there are no hard conventions

In my case, I was trying to change the icon of an executable that had no icon (i.e. had the default icon), which really is adding a resource, so I could choose any ID or name for my resource that I like and it would work (as it would be the only icon resource present in the file).

For more resources on this topic, see:

Not all of these are in C++ but they all seem to use the same underlying Windows API calls.

Regarding (2), the comment chain under the main thread makes it clear that this approach of simply dumping .ico data as a resource does not work, as the formats for icon resources is different to a .ico file. @IInspectable linked some good resources on this topic above, which I will reproduce here:

The follow-up articles on the second resource (parts 2, 3, 4) are pretty informative in general. The task I wanted to do then becomes an exercise in parsing .ico files into the icon resource format.

alex
  • 61
  • 5
  • In fact, the definitive resource on .ico and icon resources would probably be [this](https://learn.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)?redirectedfrom=MSDN). – alex May 15 '23 at 20:40
  • I was looking for an answer on which icon Explorer selects when it displays applications, so this link is helpful. I hadn't expected Win9x to do this much work. There are still a few more things to do here: When replacing an existing icon, you'll want to remove the (now unused) `RT_ICON` resources. If there are no more images in the new icon, you can just reuse the IDs and delete the remaining ones. If there are more images in the new icon, you'll have to find free `RT_ICON` resource IDs for those. Repeatedly calling `FindResource` might work. – IInspectable May 18 '23 at 09:29