You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过WINAPI获取带覆盖层标识的jumbo超大尺寸文件图标

问题描述

我需要获取带覆盖层(比如快捷方式的链接图标)的超大"jumbo"文件图标(256x256 px),效果和Windows文件资源管理器中设置为超大图标查看时的显示完全一致。

例如我有一个指向文本文件的快捷方式"a.txt.lnk",它在资源管理器中的图标如下(左下角的箭头就是我所说的覆盖层):
快捷方式图标示例

我经过大量搜索尝试寻找实现方案,使用Qt框架源码中QtWinExtras库的iconextractor示例代码做测试,但是该示例存在问题无法正常运行:目前我只能获取到带快捷方式覆盖层的小尺寸(32x32 px)图标,或者不带覆盖层的"jumbo"尺寸图标,效果如下:
带覆盖层的小尺寸图标

或者:
无覆盖层的jumbo尺寸图标

我将Qt示例代码简化后得到如下main.cpp:

#include <QtWin>

#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QGuiApplication>
#include <QImage>
#include <QPixmap>

#include <comdef.h>
#include <CommCtrl.h>
#include <commoncontrols.h>

struct PixmapEntry {
    QString name;
    QPixmap pixmap;
};

using PixmapEntryList = QVector<PixmapEntry>;

static QString formatSize(const QSize &size)
{
    return QString::number(size.width()) + u'x' + QString::number(size.height());
}

static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
{
    QPixmap result;
    static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};

    IImageList *imageList = nullptr;
    if (FAILED(SHGetImageList(iImageList, iID_IImageList, reinterpret_cast<void **>(&imageList))))
        return result;

    HICON hIcon = nullptr;

    // NOTE: the following was missing in original Qt code 
    // and therefore returned no jumbo icons at all when 
    // overlays were requested. 
    // Highest 8 bits are reverved for overlay index.
    int iconIndex = info.iIcon & 0xFFFFFF; 

    if (SUCCEEDED(imageList->GetIcon(iconIndex, ILD_TRANSPARENT, &hIcon))) {
        result = QtWin::fromHICON(hIcon);
        DestroyIcon(hIcon);
    }
    return result;
}

static PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays)
{
    enum { // Shell image list ids
        sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined
        sHIL_JUMBO = 0x4 // 256x256 (Vista or later)
    };

    struct FlagEntry {
        QString name;
        unsigned flags;
    };

    const QString nativeName = QDir::toNativeSeparators(sourceFile);
    const auto *sourceFileC = reinterpret_cast<const wchar_t *>(nativeName.utf16());

    SHFILEINFO info;
    unsigned int flags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SHELLICONSIZE;
    if (addOverlays)
        flags |= SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX; // What do these flags do? How to utilize the overlay index?

    PixmapEntryList result;

    const QString prefix = "sh_";
    ZeroMemory(&info, sizeof(SHFILEINFO));
    SHGetFileInfo(sourceFileC, 0, &info, sizeof(SHFILEINFO), flags);

    // extract standard icon
    PixmapEntry entry;
    entry.pixmap = QtWin::fromHICON(info.hIcon);
    DestroyIcon(info.hIcon);
    if (entry.pixmap.isNull()) {
        qWarning() << "Error converting icons.";
        return PixmapEntryList();
    }
    entry.name = prefix + formatSize(entry.pixmap.size());

    const int iconIndex = info.iIcon & 0xFFFFFF;
    const int overlayIconIndex = info.iIcon >> 24; // Note: some resources suggest there should be -1 to adjust for 0-based index. Really?

    qDebug() << "Obtained icon #" << iconIndex << "overlay #" << overlayIconIndex;

    result.append(entry);

    // extract jumbo icon
    const QPixmap jumbo = pixmapFromShellImageList(sHIL_JUMBO, info);
    if (!jumbo.isNull()) {
        PixmapEntry entry;
        entry.pixmap = jumbo;
        entry.name = QLatin1String("jumbo_") + formatSize(jumbo.size());
        result.append(entry);
    }

    return result;
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    const QString &sourceFile = "C:/my/path/to/some/link/a.txt.lnk";
    QString imageFileRoot = QDir::currentPath() + u'/';

    // Note: Settings true or false in the following line has no effect of overlays.
    // Small icons have always overlays, jumbo icons have never overlays.
    const PixmapEntryList pixmaps = extractShellIcons(sourceFile, true);

    for (const auto &entry : pixmaps) {
        const QString fileName = imageFileRoot + entry.name + QLatin1String(".png");
        entry.pixmap.save(fileName);
        qDebug() << "Wrote " << QDir::toNativeSeparators(fileName);
    }
    return 0;
}

iconextractor.pro配置文件内容如下:

TEMPLATE = app
TARGET = iconextractor
CONFIG += console
QT = core gui winextras
LIBS += -lshell32 -luser32
SOURCES += main.cpp

我的问题是:我不理解flags |= SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;这行代码的作用,它对标准尺寸图标没有影响(不加这行也自带覆盖层),对jumbo尺寸图标也没有影响(无论加不加都没有覆盖层)。我可以获取到图标覆盖层的索引,但不知道该怎么使用这个索引,请问如何才能获取到带覆盖层的jumbo尺寸图标?


问题解答

两个flag的作用说明

  • SHGFI_ADDOVERLAYS:仅作用于SHGetFileInfo直接返回的HICON对象,告知系统将对应覆盖层预先绘制到返回的小尺寸图标上。你不加该flag也能拿到带覆盖层小图标的原因是:系统默认会对返回的普通尺寸图标自动添加常用覆盖层。
  • SHGFI_OVERLAYINDEX:告知系统将覆盖层的1-based索引存储到SHFILEINFO结构体的iIcon字段的高8位中,你代码中通过info.iIcon >> 24拿到的就是这个索引值,只有加了这个flag才能拿到准确的覆盖层索引用于大尺寸图标的手动绘制。

无法获取带覆盖层jumbo图标的原因

系统的jumbo尺寸图标列表中仅存储无覆盖层的原始图标,覆盖层是独立的资源,不会自动合并到jumbo图标中,需要你手动利用拿到的覆盖层索引完成绘制。

具体代码修改方案

仅需要对现有代码做3处小修改即可实现需求:

  1. 修改pixmapFromShellImageList函数的签名,新增接收覆盖层索引的参数:
// 新增overlayIndex参数,默认值为0表示无覆盖层
static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info, int overlayIndex = 0)
  1. 修改imageList->GetIcon的调用参数,用INDEXTOOVERLAYMASK宏将覆盖层索引合并到绘制参数中:
// 原来的参数是ILD_TRANSPARENT,新增覆盖层mask
if (SUCCEEDED(imageList->GetIcon(iconIndex, ILD_TRANSPARENT | INDEXTOOVERLAYMASK(overlayIndex), &hIcon))) {
  1. 在调用pixmapFromShellImageList获取jumbo图标时,传入之前拿到的覆盖层索引:
// 原来的调用没有传overlayIconIndex,现在加上
const QPixmap jumbo = pixmapFromShellImageList(sHIL_JUMBO, info, overlayIconIndex);

修改完成后重新编译运行,就能拿到带完整覆盖层的256x256 jumbo尺寸图标,和资源管理器显示效果完全一致。


内容的提问来源于stack exchange,提问作者HiFile.app - best file manager

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 06:18:05