问题
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 have the content of this file in some buffer. I want to be able to create the HICON
from that buffer.
It is possible to load HICON
from the *.ico
which is placed on the hard drive, so I guess that there should be an equally simple way to do it from the memory buffer?
So far I found only 2 solutions but none of them is suitable for me.
The first one involved ATL usage and GDI+ (I'm using Rust and I don't have any bindings to GDI+).
The second one was based on usage of LookupIconIdFromDirectoryEx()
and CreateIconFromResourceEx()
. First I called LookupIconIdFromDirectoryEx()
to get the offset for the correct icon and then I tried to call CreateIconFromResourceEx()
(and CreateIconFromResource()
) to get the HICON, but in all cases I receive a NULL
value as a result, GetLastError()
returns 0
though. My usage of those functions was based on this article (I tried to pass not only 0
as a second parameter, but also the size of the array buffer, excluding the offset, but it still fails).
The only remaining solution which I have in mind is to parse the *.ico
file manually and then extract PNG images from it, then use the approach described here to create an icon from the PNG image. But it seems to be more like a workaround (Qt uses the similar approach though, maybe they were not able to find a different solution). Are there any simplier methods (maybe some WinAPI call) to get the things done?
UPD. Here is some test code which I tried (you should have an icon in order to run the example without crashes).
#include <cstdio>
#include <cstdlib>
#include <Windows.h>
#pragma comment(lib, "User32.lib")
int main()
{
// Read the icon into the memory
FILE* f = fopen("icon.ico", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
char* data = (char*)malloc(fsize + 1);
fread(data, fsize, 1, f);
fclose(f);
static const int icon_size = 32;
int offset = LookupIconIdFromDirectoryEx((PBYTE)data, TRUE, icon_size, icon_size, LR_DEFAULTCOLOR);
if (offset != 0) {
HICON hicon = CreateIconFromResourceEx((PBYTE)data + offset, 0, TRUE, 0x30000, icon_size, icon_size, LR_DEFAULTCOLOR);
if (hicon != NULL) {
printf("SUCCESS");
return 0;
}
}
printf("FAIL %d", GetLastError());
return 1;
}
回答1:
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
回答2:
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:
- 256x256 PNG
- 128x128 BMP
- 64x64 BMP
- 32x32 BMP
- 16x16 BMP
The B
icon contained 7 images, they were placed in the following order:
- 16x16 BMP
- 32x32 BMP
- 48x48 BMP
- 64x64 BMP
- 128x128 BMP
- 256x256 BMP
The results of the LookupIconIdFromDirectoryEx()
for each of the icons can be found below.
Icon A
:
- 86
- 9640
- 9640
- 9640
- 9640
Icon B
:
- 102
- 1230
- 5494
- 15134
- NOT FOUND (function failed and returned
0
) - NOT FOUND (function failed and returned
0
) - 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.
来源:https://stackoverflow.com/questions/41379285/load-hicon-from-the-buffer-ico-file