使用WINAPI为巨型图标添加文件图标叠加。

3
我需要获取带有叠加图标(例如快捷方式的链接图标)的大型“巨型”文件图标(256x256像素),基本上与在Windows文件资源管理器中设置为以超大图标查看项目时所见到的相同。
例如,我有一个指向文本文件的快捷方式“a.txt.lnk”,它在文件资源管理器中具有此图标。这就是我想要获取的内容(是的,左下角的箭头就是我所说的叠加图标):

enter image description here

我非常努力地谷歌了一下,找到了如何做这件事的方法。
为了实验代码,我使用了Qt框架中的QtWinExtras库中的iconextractor示例。不幸的是,它有一些问题,无法正常工作。我所能做的就是获取带有快捷方式叠加的小图标(32x32像素)或没有叠加的“巨型”图标。这个:

enter image description here

或者这样:

enter image description here

我将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; 的作用是什么。它对标准图标没有影响(即使没有这行代码,它们也包含覆盖层),对巨型图标也没有影响(它们本来就不包含覆盖层)。显然我可以获取图标覆盖层的索引,但我不知道该如何使用它。如何获取带有覆盖层的巨型图标?

注意:我已经创建了一个与此问题相关的Qt错误票 https://bugreports.qt.io/browse/QTBUG-96025 - HiFile.app - best file manager
1个回答

2
我认为我已经取得了突破。关键是使用 IImageList::GetOverlayImage 将图标索引的最高8位转换为叠加图标索引,并使用实际索引检索它作为图标(与基本图标相同的方式)。因此,这是加载基本图标、叠加图标并将叠加图标绘制到基本图标中的更改函数。
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;
    int iconIndex = info.iIcon & 0xFFFFFF;

    if (FAILED(imageList->GetIcon(iconIndex, ILD_TRANSPARENT, &hIcon))) {
        return result;
    }

    result = QtWin::fromHICON(hIcon);
    DestroyIcon(hIcon);

    int overlayIndex;
    if (FAILED(imageList->GetOverlayImage(info.iIcon >> 24, &overlayIndex)))
    {
        return result;
    }

    if (FAILED(imageList->GetIcon(overlayIndex, ILD_TRANSPARENT, &hIcon)))
    {
        return result;
    }

    QPixmap overlay = QtWin::fromHICON(hIcon);
    DestroyIcon(hIcon);

    // TODO: better placement and resizing of overlay within the icon
    QPainter painter(&result);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);
    int w = overlay.width() / 2;
    int h = overlay.height() / 2;
    painter.drawPixmap(0, result.height() - h, w, h, overlay);

    return result;
}

覆盖层有点太大了,但没关系,我可以调整代码使其更小并且位置不那么随意。(也许使用IImageList::GetImageRect)这是我目前的结果,我很满意:

enter image description here


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