iooiau.net

ファイルの大きなアイコンを取得する

Windows XP では 48x48、Vista では 256x256 という大きなサイズのアイコンがサポートされました。
ファイルからこれらの大きなサイズのアイコンを取得する方法です。

従来の 32x32 や 16x16 のアイコンは、SHGetFileInfo 関数で取得することができます。


SHFILEINFO sfi;
if (SHGetFileInfo(pszFileName, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON)) {
    // sfi.hIcon にアイコンが取得される

    ...

    // アイコンが不要になったら破棄する
    DestroyIcon(sfi.hIcon);
}

SHGFI_LARGEICON の代わりに SHGFI_SMALLICON を指定すれば、小さいアイコンを取得できます。
しかし、SHGetFileInfo ではこの2種類のアイコンしか取得することができません。

より大きなサイズのアイコンを取得したい場合、Windows が持っているアイコンの画像リストを取得して、その中から必要なアイコンを抽出します。


#include <windows.h>
#include <tchar.h>
#include <shlobj.h>
#include <shellapi.h>
#include <commoncontrols.h>

HICON GetBigIcon(LPCTSTR pszFileName)
{
    // システムアイコンのインデックスを取得する
    SHFILEINFO sfi;
    if (!SHGetFileInfo(pszFileName, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX))
        return NULL;

#if Windows XP 以前も対応する場合

    // SHGetImageList 関数のアドレスを取得する
    HMODULE hLib = GetModuleHandle(TEXT("shell32.dll"));
    if (hLib == NULL)
        return NULL;
    HRESULT (WINAPI *SHGetImageListFunc)(int iImageList, REFIID riid, void **ppv);
    SHGetImageListFunc pSHGetImageList = (SHGetImageListFunc)GetProcAddress(hLib, (LPCSTR)727);
    if (pSHGetImageList == NULL)
        return NULL;

    // 画像リストを取得する
    IImageList *pImageList;
    if (pSHGetImageList(SHIL_EXTRALARGE, IID_IImageList, (void**)&pImageList) != S_OK)
        return NULL;

#else // Vista 以降のみ対応で良い場合

    // 画像リストを取得する
    IImageList *pImageList;
    if (SHGetImageList(SHIL_EXTRALARGE, IID_IImageList, (void**)&pImageList) != S_OK)
        return NULL;

#endif

    // アイコンを取得する
    HICON hIcon;
    if (pImageList->GetIcon(sfi.iIcon, ILD_TRANSPARENT, &hIcon) != S_OK)
        hIcon = NULL;

    // 画像リストを解放する
    pImageList->Release();

    /* このアイコンは使い終わったら DestroyIcon で破棄してね */
    return hIcon;
}

SHGetImageList 関数は、Windows XP 以降の shell32.dll に存在しています。
以前のドキュメントには、名前ではエクスポートされていないので序数でアドレスを取得しろという内容が書いてありましたが、今はその記述は消えています。
ですので、 GetProcAddress(hLib, "SHGetImageList") のように名前で取得しても問題ないかも知れません。
追記:Windows Vista 以降に動作を限定する場合、動的にアドレスを取得せず、素直に SHGetImageList を呼ぶだけで良いでしょう。

SHGetImageList に SHIL_EXTRALARGE (2) を指定すると 48x48、SHIL_JUMBO (4) を指定すると 256x256 のアイコンを取得できます。
ただし、48x48 というのは必ずという訳ではなく、画面解像度の設定によって違うサイズになることもあります。

SHGetImageList で取得した IImageList は、不要になった時に Release で解放します。

なお、IImageList へのポインタは HIMAGELIST にキャストして使うことができます。
例えば、以下のようにも出来るわけです。


HIMAGELIST himl = (HIMAGELIST)pImageList;
hIcon = ImageList_GetIcon(himl, sfi.iIcon, ILD_TRANSPARENT);

HIMAGELIST の実体は IImageList へのポインタなのでしょうね。