首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Qt 獲取文件超大圖標 (256×256):SHGetImageList + SHIL_JUMBO + DLL 互調實戰

     編輯:關於C語言

問題背景

Qt QFileIconProvider 僅支持 SHGFI_SMALLICON (16×16) 和 SHGFI_LARGEICON (32×32)。**無法直接獲取 48×48、256×256 (Jumbo) 等超大圖標**,且 QIcon::pixmap(128,128) 只是拉伸 32×32,畫質模糊。

核心原理:Windows 系統圖像列表

  • Shell 將所有文件圖標統一存儲在**系統圖像列表** (System Image List) 中,由 HIMAGELIST 句柄管理。
  • SHGetFileInfo 僅能獲取**圖標在圖像列表中的索引** (iIcon)。
  • 獲取大圖標需直接調用 SHGetImageList 獲取 IImageList COM 接口,再按索引提取 HICON。
  • 圖像列表類型常量:
    • SHIL_SMALL (16×16)
    • SHIL_LARGE (32×32)
    • SHIL_EXTRALARGE (48×48, Vista+)
    • SHIL_JUMBO (256×256, Vista+)

方案架構:VS DLL 導出 + Qt 動態加載調用

Qt (MinGW/MSVC) 與 Windows SDK 頭文件/庫鏈接存在兼容性差異,**最穩妥**:用 **VS 2010+ 編譯純 C DLL** 封裝 Windows API,Qt 用 QLibrary 動態加載調用。

1. VS DLL 端 (導出 getJumboIcon)


// IconExtractor.cpp (VS 項目: Win32 DLL, Release/x64 對應 Qt 編譯器位數)
#include 
#include 
#include  // SHGetImageList
#include          // SHGetFileInfo
#pragma comment(lib, "Comctl32.lib")

extern "C" __declspec(dllexport) HICON getJumboIcon(const wchar_t* filePath) {
    // 1. 獲取系統圖標索引
    SHFILEINFOW sfi = {0};
    SHGetFileInfoW(filePath, -1, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
    int iconIndex = sfi.iIcon;

    // 2. 獲取 Jumbo 圖像列表 (Vista+)
    IImageList* pImageList = nullptr;
    HRESULT hr = SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&pImageList);
    if (FAILED(hr) || !pImageList) return nullptr;

    // 3. 從列表提取 HICON
    HICON hIcon = nullptr;
    hr = pImageList->GetIcon(iconIndex, ILD_TRANSPARENT, &hIcon);
    pImageList->Release();

    if (SUCCEEDED(hr)) return hIcon;
    return nullptr;
}

關鍵點:

  • SHGetFileInfoW 寬字符版,-1 表示不檢查文件屬性僅取索引,極快。
  • SHIL_JUMBO 需 Vista/Win7 以上,XP 返回失敗可回退 SHIL_EXTRALARGE 或 SHIL_LARGE。
  • ILD_TRANSPARENT 保持透明背景。
  • 調用者**必須**調用 DestroyIcon(hIcon) 釋放資源。

2. Qt 端調用


// IconHelper.h
#include 
#include 
#include  // HICON 定義

typedef HICON (*GetJumboIconFunc)(const wchar_t*);

QPixmap getFileJumboIcon(const QString& filePath) {
    // 1. 加載 DLL (放置於應用目錄下的 plugins/icon_extractor.dll)
    QLibrary lib(qApp->applicationDirPath() + "/plugins/icon_extractor.dll");
    if (!lib.load()) {
        qWarning() << "Load DLL failed:" << lib.errorString();
        return QPixmap();
    }

    // 2. 解析函數
    auto func = (GetJumboIconFunc)lib.resolve("getJumboIcon");
    if (!func) {
        qWarning() << "Resolve getJumboIcon failed";
        return QPixmap();
    }

    // 3. 調用 (Qt QString -> wchar_t*)
    HICON hIcon = func(reinterpret_cast(filePath.utf16()));
    if (!hIcon) return QPixmap();

    // 4. HICON -> QPixmap (Qt 5.2+ 支持)
    QPixmap pixmap = QPixmap::fromWinHICON(hIcon);

    // 5. 釋放 HICON (關鍵!否則 GDI 對象洩露)
    DestroyIcon(hIcon);

    return pixmap; // 可直接 setIcon / 保存 / 縮放
}

3. 使用示例


QString exePath = "C:/Windows/notepad.exe";
QPixmap jumbo = getFileJumboIcon(exePath);
if (!jumbo.isNull()) {
    ui->label->setPixmap(jumbo.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    jumbo.save("notepad_256.png"); // 保存原始 256x256
}

進階擴展:支持多尺寸選擇


// DLL 端增加 size 參數
extern "C" __declspec(dllexport) HICON getIconBySize(const wchar_t* filePath, int sizeType) {
    // sizeType: 0=Small(16), 1=Large(32), 2=ExtraLarge(48), 3=Jumbo(256)
    static const int SHIL_TYPES[] = {SHIL_SMALL, SHIL_LARGE, SHIL_EXTRALARGE, SHIL_JUMBO};
    if (sizeType < 0 || sizeType > 3) sizeType = 1;
    // ... 同前邏輯,使用 SHIL_TYPES[sizeType]
}

避坑指南

坑點解決
DLL 位數不匹配Qt 編譯器 (MinGW 64 / MSVC 64) **必須**與 DLL 編譯目標 (x64) 一致
中文路徑亂碼Qt 傳 filePath.utf16() (UTF-16),DLL 用 wchar_t* / SHGetFileInfoW
HICON 洩露**必須**調用 DestroyIcon,Qt 端調用後立即釋放
XP 系統無 JumboDLL 端 FAILED(hr) 時回退 SHIL_EXTRALARGE 或 SHIL_LARGE
管理員權限文件若需提取需提權文件圖標,DLL 需配清單以最高權限運行,或 Qt 進程提權

總結

通過 **VS DLL 封裝 SHGetImageList(SHIL_JUMBO) + Qt QLibrary 動態調用 + QPixmap::fromWinHICON**,完美解決 Qt 獲取 256×256 超大圖標問題,畫質無損,架構解耦,生產環境驗證穩定。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved