I\'m just wondering, if there an API in Windows for loading the HICON
from the byte array (buffer)? Let\'s say that I downloaded an *.ico
file and
I found a solution. Actually after a bit of research it turned out that the code, which I placed inside the sample, is indeed correct.
There is a bug in WinAPI function LookupIconIdFromDirectoryEx()
. I've noticed that for some icons I can get the correct icon and set it up, but for others it fails either on the later stage CreateIconFromResourceEx()
, or earlier on LookupIconIdFromDirectoryEx()
. I've noticed that sometimes the function fails to find the icon even though the icon is inside a file. Sometimes the function returned the same value for the different icons inside an icon file.
I made several rounds of tests and parsed the format of each icon file myself based on the format definition. Then I compared the actual offsets to the values returned by LookupIconIdFromDirectoryEx()
.
Let's say we have 2 icons: A
and B
.
The A
icon in my case contained 5 images, the entries inside the ICO file were placed in the following order:
The B
icon contained 7 images, they were placed in the following order:
The results of the LookupIconIdFromDirectoryEx()
for each of the icons can be found below.
Icon A
:
Icon B
:
0
)0
)0
)I parsed the actual format, according to the definition in wikipedia (the tables below are contain the icon entries, each row is a separate entry, each column is a field for this entry) for both icon files.
The actual layout of A
is:
W H * * * ** SIZE OFFSET
------------------------------------------------
0 0 0 0 1 32 43253 86
128 128 0 0 1 32 67624 43339
48 48 0 0 1 32 9640 110963
32 32 0 0 1 32 4264 120603
16 16 0 0 1 32 1128 124867
The actual layout of B
is:
W H * * * ** SIZE OFFSET
------------------------------------------------
16 16 0 0 0 32 1128 102
32 32 0 0 0 32 4264 1230
48 48 0 0 0 32 9640 5494
64 64 0 0 0 32 16936 15134
128 128 0 0 0 32 67624 32070
0 0 0 0 0 32 270376 99694
So you can clearly see, that in case of A
only the offset for the first image was idenfied correct, offsets for other images were incorrect and equal to the ... size of the 3rd image (??), maybe just a coincidence.
In case of the second image all offsets were correct until we reached 128x128 image, which was not even found.
The common thing between those 2 cases is that the function started to behave strange after parsing 128x128 icon, and here is an interesting thing - look on the size of 128x128 icon and compare it to the size of the other images. In both cases the size of the 128x128 image does not fit in 2 bytes. Right after parsing the icon entry in which the size was bigger than 2 bytes, the function behavior is undefined. Judging from this data I can assume that somewhere in the code they expect that the size of the icon cannot be bigger than 2 bytes. In case if the size is bigger, the behavior is undefined.
I used ImageMagick to reassemble a new icon which does not have such image inside and now the function works correct in all cases.
So I can definitely say that there is a bug inside LookupIconIdFromDirectoryEx()
implementation.
UPD. Icons can be found here: A icon, B icon.
CreateIconFromResourceEx((PBYTE)data + offset, 0, ...)
The second parameter should not be zero. Windows doesn't know how far it can read the buffer without causing buffer overflow. Apparently Windows does allow for this error in some cases, but maybe it's not prepared in the case of PNG files, it stops when it doesn't see a BITMAPINFOHEADER
Just provide the maximum available buffer size, that should solve the problem with PNG files:
CreateIconFromResourceEx((PBYTE)data + offset, fsize - offset, ...)
Documentation says that LookupIconIdFromDirectoryEx
expects resource data. This API does appear to work with icon files, it returns the offset for the first icon. Either way it doesn't appear to have a bug based on what the documentation says.
It's better to calculate the offset manually. It appears you already know how to calculate the offset values. You can simply calculate the offset as follows, based on ICONDIRENTRY
WORD icon_count = 0;
fseek(f, 2 * sizeof(WORD), SEEK_SET);
fread(&icon_count, sizeof(WORD), 1, f);
int offset = 3 * sizeof(WORD) + sizeof(ICONDIRENTRY) * icon_count;
sizeof(ICONDIRENTRY)
is 16. The icon file starts with 3 WORD
values, the third value is icon_count
, followed sizeof(ICONDIRENTRY) * icon_count
bytes, followed by the bytes for the first HICON