首页 文章

从缓冲区加载HICON(* .ico文件)

提问于
浏览
1

我只是想知道,如果在Windows中有一个API用于从字节数组(缓冲区)加载 HICON ?假设我下载了一个 *.ico 文件,我将该文件的内容放在某个缓冲区中 . 我希望能够从该缓冲区创建 HICON .

可以从放置在硬盘驱动器上的 *.ico 加载 HICON ,所以我想从内存缓冲区应该有一个同样简单的方法吗?

到目前为止,我发现只有2种解决方案,但它们都不适合我 .

第一个involved ATL usage and GDI+(我'm using Rust and I don't与GDI有任何绑定) .

第二个是基于 LookupIconIdFromDirectoryEx()CreateIconFromResourceEx() 的使用 . 首先,我调用 LookupIconIdFromDirectoryEx() 来获取正确图标的偏移量,然后我尝试调用 CreateIconFromResourceEx() (和 CreateIconFromResource() )来获取HICON,但是在所有情况下我都收到 NULL 值,但是 GetLastError() 返回 0 . 我对这些函数的使用是基于this article(我试图不仅传递 0 作为第二个参数,而且还传递数组缓冲区的大小,不包括偏移量,但它仍然失败) .

我想到的唯一剩下的解决方案是手动解析 *.ico 文件,然后从中提取PNG图像,然后使用here描述的方法从PNG图像创建一个图标 . 但它似乎更像是一种解决方法(Qt使用类似的方法,但也许他们无法找到不同的解决方案) . 是否有任何更简单的方法(可能是一些WinAPI调用)来完成工作?

UPD. 这是我尝试的一些测试代码(你应该有一个图标,以便运行没有崩溃的例子) .

#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;
}

2 回答

  • -2

    CreateIconFromResourceEx((PBYTE)数据偏移量,0,...)

    第二个参数不应为零 . Windows没有准备好在PNG文件的情况下,它在没有看到 BITMAPINFOHEADER 时停止

    只需提供最大可用缓冲区大小,即可解决PNG文件的问题:

    CreateIconFromResourceEx((PBYTE)data + offset, fsize - offset, ...)
    

    文档说 LookupIconIdFromDirectoryEx 期望资源数据 . 此API似乎与图标文件一起使用,它返回第一个图标的偏移量 . 无论哪种方式,它似乎都没有基于文档所说的错误 .

    最好手动计算偏移量 . 您似乎已经知道如何计算偏移值 . 您可以根据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) 是16.图标文件以3个 WORD 值开头,第三个值是 icon_count ,后跟 sizeof(ICONDIRENTRY) * icon_count 个字节,后跟第一个 HICON 的字节

  • 1

    我找到了解决方案 . 实际上经过一些研究后发现,我放在样本中的代码确实是正确的 .

    WinAPI函数 LookupIconIdFromDirectoryEx() 中存在错误 . 我注意到,对于某些图标,我可以获得正确的图标并进行设置,但对于其他图标,它会在后期 CreateIconFromResourceEx() 或早期的 LookupIconIdFromDirectoryEx() 上失败 . 我注意到,即使图标在文件中,有时功能也无法找到图标 . 有时,函数为图标文件中的不同图标返回相同的值 .

    我做了几轮测试,并根据format definition自己解析每个图标文件的格式 . 然后我将实际偏移量与 LookupIconIdFromDirectoryEx() 返回的值进行了比较 .

    假设我们有2个图标: AB .

    在我的案例中, A 图标包含5个图像,ICO文件中的条目按以下顺序放置:

    • 256x256 PNG

    • 128x128 BMP

    • 64x64 BMP

    • 32x32 BMP

    • 16x16 BMP

    B 图标包含7张图片,按以下顺序放置:

    • 16x16 BMP

    • 32x32 BMP

    • 48x48 BMP

    • 64x64 BMP

    • 128x128 BMP

    • 256x256 BMP

    每个图标的 LookupIconIdFromDirectoryEx() 的结果可以在下面找到 .

    图标 A

    • 86

    • 9640

    • 9640

    • 9640

    • 9640

    图标 B

    • 102

    • 1230

    • 5494

    • 15134

    • 未找到(功能失败并返回 0

    • 未找到(功能失败并返回 0

    • 未找到(功能失败并返回 0

    我解析了实际的格式,根据definition in wikipedia(下面的表格包含图标条目,每行是一个单独的条目,每列都是此条目的字段)两个图标文件 .

    A 的实际布局是:

    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
    

    B 的实际布局是:

    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
    

    所以你可以清楚地看到,在 A 的情况下,只有第一张图像的偏移被识别为正确,其他图像的偏移是不正确的并且等于...第三张图像的大小(??),可能只是巧合 .

    在第二张图像的情况下,所有偏移都是正确的,直到我们达到128x128图像,甚至没有找到 .

    这两种情况之间的共同点是,在解析128x128图标后,函数开始表现得很奇怪,这是一个有趣的事情 - 查看128x128图标的大小,并将其与其他图像的大小进行比较 . 在这两种情况下,128x128图像的大小都不适合2个字节 . 解析大小超过2个字节的图标条目后,函数行为未定义 . 从这些数据来看,我可以假设在代码中的某个地方,他们期望图标的大小不能大于2个字节 . 如果大小更大,则行为未定义 .

    我使用ImageMagick重新组装一个新的图标,里面没有这样的图像,现在该功能在所有情况下都正常工作 .

    所以我可以肯定地说 LookupIconIdFromDirectoryEx() 实现中存在一个错误 .

    UPD . 图标可以在这里找到:A iconB icon .

相关问题