Load HICON from the buffer (*.ico file)

后端 未结 2 578
南方客
南方客 2021-01-16 11:58

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

相关标签:
2条回答
  • 2021-01-16 12:05

    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:

    1. 256x256 PNG
    2. 128x128 BMP
    3. 64x64 BMP
    4. 32x32 BMP
    5. 16x16 BMP

    The B icon contained 7 images, they were placed in the following order:

    1. 16x16 BMP
    2. 32x32 BMP
    3. 48x48 BMP
    4. 64x64 BMP
    5. 128x128 BMP
    6. 256x256 BMP

    The results of the LookupIconIdFromDirectoryEx() for each of the icons can be found below.

    Icon A:

    1. 86
    2. 9640
    3. 9640
    4. 9640
    5. 9640

    Icon B:

    1. 102
    2. 1230
    3. 5494
    4. 15134
    5. NOT FOUND (function failed and returned 0)
    6. NOT FOUND (function failed and returned 0)
    7. NOT FOUND (function failed and returned 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.

    0 讨论(0)
  • 2021-01-16 12:13
    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

    0 讨论(0)
提交回复
热议问题