提取图标和提取关联图标之间的区别?需要提取特定大小的图标。

3
说,如果我想从存储的Windows可执行文件中提取一个图标。我可以通过在Visual Studio中打开它来获取该图标ID:

enter image description here

那我会对一个48x48大小的图标感兴趣:

enter image description here

我的假设是这样做的:

所以我的假设是:

HICON hIcons[4];
::ExtractIconEx(L"mstsc.exe", -13011, hIcons, NULL, 4);
hIconLogo = hIcons[3];

但是当我运行它时,该方法只返回3个图标:

enter image description here

"其中只有一个是我需要的32x32版本。"
"然后,我找到了ExtractAssociatedIconEx API,并进行了如下调用:"
WORD wIcnId = -13011;
WORD wIcnInd = 3;
hIconLogo = ::ExtractAssociatedIconEx(hInst, L"mstsc.exe", &wIcnInd, &wIcnId);

但是它也给了我一些我没有预料到的其他图标。
那么这两个API之间有什么区别?我做错了什么吗?

尝试使用LoadImage获取特定大小的图标。 - Jonathan Potter
@JonathanPotter:谢谢。虽然我不确定我是否正确地编写了它。如果我这样写 ::LoadImage(::LoadLibrary(L"mstsc.exe"), MAKEINTRESOURCE(13011), IMAGE_ICON, 48, 48, LR_LOADFROMFILE); 它会抛出访问冲突异常,但是 ::LoadImage(::LoadLibrary(L"mstsc.exe"), L"#13011", IMAGE_ICON, 48, 48, LR_LOADFROMFILE); 返回值是 NULL - c00000fd
1个回答

22

ExtractIconEx函数只能返回两种尺寸的图标:大图标和小图标。这些是相对大小,由环境定义。 "大"图标经典上是32x32像素,但在某些系统配置中可能更大。 "小"图标经典上是16x16像素,但同样适用于相同的警告。唯一的保证是“小”图标比“大”图标要小。如果您想知道在您的系统上实际大小,您可以使用SM_CXICONSM_CYICON来调用GetSystemMetrics函数以获取“大”图标,或者使用SM_CXSMICONSM_CYSMICON获取“小”图标。

操作系统在内部的各处都使用“小”和“大”图标;大多数API仅处理“小”和“大”(有时也称为“大”)图标。例如,当你 为窗口设置图标 时,你只能设置“小”图标或“大”图标。这是你唯一的两个选择。 ExtractIconEx 函数将 phIconLarge 参数设置为指向一组大图标句柄的指针。 phIconSmall 参数设置为指向一组小图标句柄的指针。由于你对 phIconSmall 参数传递了 NULL,所以没有得到任何小图标。hIcons 填充了文件中“大”图标的句柄,这些图标在您的系统上是不同深度的32x32图标。 ExtractAssociatedIcon函数(以及它的Ex版本)只返回“大”图标。因此,当你调用它时,应该会得到与调用ExtractIconEx相同的结果。我不确定你是否在说它提供了不同的结果。这可能与索引有关。负索引对ExtractIconEx有特殊含义,但我不确定它们对ExtractAssociatedIcon是否有效。文档没有给出太多提示。
虽然SHGetFileInfo函数在许多方面更加强大,包括从任何文件系统对象中提取图标的能力,但它具有相同的基本限制:它只提供SHGFI_LARGEICONSHGFI_SMALLICON的选择。
"If you need to extract icons of custom sizes (i.e., something other than the system's "small" and "large" sizes), then you'll need to do more work. There are essentially two options:
1. 调用SHGetImageList函数,这是另一个shell帮助函数,但它检索包含图标的shell图像列表。它为图标大小提供了更多选项: SHIL_SMALL(通常为16x16),SHIL_LARGE(通常为32x32),SHIL_EXTRALARGE(通常为48x48)和SHIL_JUMBO(通常为256x256-仅适用于Vista及更高版本)。因此,如果您要求SHIL_EXTRALARGE,则会获得您要查找的48x48图标。"
你仍需要使用SHGetFileInfo函数,但这次是为了检索所需图标在shell图像列表中的索引。使用SHGFI_SYSICONINDEX选项来检索。

完全未经测试的示例代码,从未被编译器触及:

    HICON ExtractExtraLargeIcon(LPCTSTR pszPath)
    {    
        // Determine the index of the desired icon
        // in the system image list.
        SHGETFILEINFO sfi;
        SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
        
        // Retrieve the system image list.
        // (To get 48x48 icons, we use `SHIL_EXTRALARGE`.)
        IImageList* piml;
        if (SHGetImageList(SHIL_EXTRALARGE, IID_IImageList, (void**)&piml) == S_OK)
        {
            HICON hIcon;
            if (piml->GetIcon(sfi.iIcon, ILD_TRANSPARENT, &hIcon) == S_OK)
            {
               return hIcon;
            }
        }
        
        // Oops! We failed.
        return NULL;
    }
  1. 您的另一个选择是自己从文件中提取所需大小的图标。尽管图标资源格式已经有了很好的文档说明,并且各种人已经使用这些知识编写了一堆丑陋的提取代码并发布在互联网上,但有一种更简单的方法:SHDefExtractIcon

正如Raymond Chen之前所述,SHDefExtractIcon是您更强大的后备方案,如果IExtractIcon::Extract(上面的代码示例尝试使用此函数)失败。该函数的强大之处在于它的nIconSize参数,该参数指定要提取的图标的实际大小。

根据Raymond的示例进行调整:

    HICON ExtractArbitrarySizeIcon(LPCTSTR pszPath, int size)
    {    
        HICON hIcon;
        if (SHDefExtractIcon(pszPath, 1, 0, &hIcon, NULL, size) == S_OK)
        {
            return hIcon;
        }
        return NULL;  // failure
    }

无论你做什么,记住每当 API 函数返回 HICON 时,它都会将该资源的所有权转移给你。这意味着,当你完成对图标的使用后,必须通过调用 DestroyIcon 函数来销毁它,以避免泄漏。

是啊,我希望早点看到这个。我已经继续前进了,所以无法测试你的代码。但我会因为信息丰富而给它点赞。在我的情况下,我决定坚持使用最初API提供的较小的32x32图标。 - c00000fd
此外,Raymond 已经更改了 URL,现在是 https://devblogs.microsoft.com/oldnewthing/20140501-00/?p=1103。 - Yury Schkatula
@YurySchkatula 谢谢,已更新链接。是的,MSDN总是喜欢移动有用的内容。他们从未意识到酷的URI不变的信息。如果您在问题或答案中看到像这样的失效链接,并且您可以修复,请毫不犹豫地提交编辑。无论是对我的帖子还是其他人的帖子。 - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接