HICON from Memory – Fixed

It gets worse from where I left it. My solution was good for retrieving the one and only icon image in the .ico file. It fulfilled my needs, so I didn’t have to check it further. But Jaine left a comment where she says my code doesn’t work for her icons. So I created a dummy project and tried my code with an icon file which contains several images and tried to get one of them, and to my surprise it didn’t work. The reason I decided to write about this issue in the first place, was because I thought I had a good solution that might interest other people. Specifically the implementation that I am dependent only on standard API’s, nothing undocumented, etc…

First thing I should do is explain why my code doesn’t work for several images in the same file. So, if you took a look at the article I directed you in the other post, “Icons in Win32”, you’ll notice, and I said that earlier too, that there are two structures of the images of the icon: in-memory and in-file. My assumption was that they are of the same size, but I didn’t notice that the in-memory structure is two bytes lesser than the in-file structure. To be accurate, the in-file structure named ICONDIRECTORY, according to that article, is 0x10 bytes in size. And the in-memory structure named GRPICONDIRENTRY, is 0xe bytes in size. The different is in the last field, which I said was the same in both structures, which is the Id and Offset. The Id is a word sized and the Offset is dword sized (so you can point to the whole file, > 64kb). So what happened was that LookupIconIdFromDirectoryEx scanned the bits we fed it with the sizeof(GRPICONDIRENTRY) and the file we read is using the ICONDIRECTORY structure which is longer. Thus, every loop (if you have more than one image in the .ico file), the offset to read the next structure of the image was -2 behind… and things messed up, of course. Note that icons in resources are stored as in-memory, recall you pass ID using MAKEINTRESOURCE to LoadImage…

I really hoped to find a solution which uses the standard API, but without success. Maybe GDI+ will do the job, but I didn’t try and I don’t wanna use GDI+ in my case…but you should give it a shot, with IStream and other not-pretty stuff. :) Anyways, I couldn’t avoid a hacky solution, this way or another, it seems we will have to rely on that article and hope MS won’t change their icon format (LOL, never know). So this is my really hacky solution:

void FixIconToResource(PBYTE pbuf)
{
unsigned short i, count = *(unsigned short*)&pbuf[4];

for (i = 1; i < count; i++) {

// 6 to skip GRPICONDIRENTRY header. 0x10=in-file size, 0xe=in-mem size.

memcpy(pbuf + 6 + (i * 0xe), pbuf + 6 + (i * 0x10), 0xe);
}
}

It has to be called with the buffer of the icon and before it is passed to LookupIconIdFromDirectoryEx. It assumes the icon is valid and that I can access the structures inside it. What this code actually does is repositioning the ICONDIRECTORY structures to be in their appropriate place as GRPICONDIRENTRY (just copying them 2 bytes backward, overriding the 2 bytes of the offset). This solution seems to work well, but it has two drawbacks, the easy-to-notice one is that I alter the buffer in-place. But I guess that’s not a biggy. The other problem is that while copying the structure, I actually convert it, thus chopping the last 2 bytes of the Offset (of the image inside the file…) and making it a word sized value. So it means that if you have icon files that are bigger than 64kb, this solution won’t work…

The funny thing (or really sad) is the reason my code still worked for the first icon without this fix. The thing is that the Id  (word) and Offset(dword) start on the same offset in both structures. So if you have a dword and you read only a word from it, you will get the low-word. Luckily, we’re on x86 architecture and the little-endian behavior made it working for me, otherwise I would probably end up with Offset of zero…

I thought it’s important to, at least, say a few words about the ‘best’ solution. That will be like what LoadImage actually does deep inside its code… constructing a fake GRPICONDIR (that’s like the icon resource inside a PE) and filling in all fields from the in-file structures to the GRPICONDIRENTRY. But this is not enough, because we still want to support any file-size. So you will pass in the index of the image inside the icon group. Then when you are done converting all images, you will call LookupIconIdFromDirectoryEx which will return the correct Id (which is actually the index to the icon) and what you are left to do is seeking to the in-file structures as an array with that index, and get the offset to the image, passing that to the CreateIcon…

Thanks Jaine, without you it wouldn’t have happened… :P

3 Responses to “HICON from Memory – Fixed”

  1. Janine says:

    Now I have a clear view on this theme. I’m gonna try it with the ‘best’ solution you offered… :-)

    Thanks for fast reply and for helping me to get a better understanding on this. And sorry for the low-level english… *hrm*

    Greets,

    J.

  2. Linda says:

    This method fails with the ico file >64KB, and also with (256×256 32bpp) packed icons.

Leave a Reply