Search code examples
winapishell-extensions

File icons blurry when loaded with an icon handler shell extension


I have a shell extension with an icon handler that sets a file type's icons to a green or red icon, based on file contents. The icons seem to work properly except they are really blurry when using large icons, as if they were zoomed in from a really small size. The icon .ico files contain all image sizes from 256x256 to 16x16.

Icons are blurry

I am using a very basic icon handler, but maybe there could still be some problem with caching or something. How could I make sure the icons are loaded properly?

HRESULT icon_handler::GetIconLocation(UINT u_flags, PWSTR psz_icon_file, UINT cch_max, int* pi_index, UINT* pw_flags)
{
    *pw_flags = GIL_NOTFILENAME | GIL_DONTCACHE;
    return S_OK;
}

HRESULT icon_handler::Extract(PCWSTR psz_file, UINT n_icon_index, HICON* phicon_large, HICON* phicon_small, UINT n_icon_size)
{
    int icon = ICON_GREEN;
    if (m_icon_color_ == 1) {
        icon = ICON_RED;
    }

    if (phicon_large != nullptr)
    {
        const int large_size = LOWORD(n_icon_size);
        *phicon_large = HICON(LoadImageW(global_h_instance, MAKEINTRESOURCE(icon), IMAGE_ICON, large_size, large_size, 
            LR_DEFAULTCOLOR));
    }
    if (phicon_small != nullptr)
    {
        const int small_size = HIWORD(n_icon_size);
        *phicon_small = HICON(LoadImageW(global_h_instance, MAKEINTRESOURCE(icon), IMAGE_ICON, small_size, small_size,
            LR_DEFAULTCOLOR));
    }

    return S_OK;
}

When logging with DebugView the icon handler appears to request appropriate sizes:

[30100] phicon_large size:
[30100] 256
[30100] phicon_small size:
[30100] 16

Edit: As per @Anders, if I check the size of the image loaded with LoadImage it also appears to be correct:

*phicon_large = HICON(LoadImageW(global_h_instance, MAKEINTRESOURCE(icon), IMAGE_ICON, large_size, large_size, 
            LR_DEFAULTCOLOR));
ICONINFOEXW info = {sizeof(ICONINFOEXW)};
GetIconInfoEx(*phicon_large, &info)
BITMAP bmp;
GetObjectW(info.hbmMask, sizeof(BITMAP), &bmp);
OutputDebugStringW(L"Icon size:");
OutputDebugStringW(std::to_wstring(bmp.bmWidth).c_str());
[12376] phicon_large size:
[12376] 256
[12376] Icon size:
[12376] 256
[12376] phicon_small size:
[12376] 16
[12376] Icon size:
[12376] 16


Solution

  • I've seen this kind of information from past documents.I can't guarantee that this information is accurate now.

    nIconSize

    Indicates the desired sizes of the icons. The high word is the dimensions (both height and width, since they're always the same) of the small icon, and the low word holds the dimensions of the of the large icon. Under normal circumstances, the small icon size will be 16. The large icon will usually be 32 or 48, depending on which view mode Explorer is in - 32 for large icon mode, 48 for tile mode.

    It seems that IExtract IconA:: Extract can only extract icons in standard sizes.

    On the other hand, refer Raymond Chen's old thing,

    if you ask IExtract­Icon::Extract to extract an icon at a particular size, the function can return S_FALSE.The Extract­Icon and Extract­Icon­Ex functions don’t let you specify a custom size, and Load­Image doesn’t work with icon indices (only resource IDs).

    Therefore,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.

    Call the SHGetImageList function, which is another shell helper function, but one that retrieves a shell image list containing icons. It gives you far more options for icon sizes: SHIL_SMALL (generally 16x16), SHIL_LARGE (generally 32x32), SHIL_EXTRALARGE (generally 48x48), and SHIL_JUMBO (generally 256x256—only on Vista and later). So if you ask for SHIL_EXTRALARGE, you'll get the 48x48 icons that you're looking for.

    You'll still need the SHGetFileInfo function here, but this time it will be to retrieve the index of the desired icon in the shell image list. Retrieve that with the SHGFI_SYSICONINDEX option.

    Completely untested sample code, never touched by a compiler:

    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 256x256 icons, we use `SHIL_JUMBO`.)
        IImageList* piml;
        if (SHGetImageList(SHIL_JUMBO, 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;
    }
    

    More Details, please refer :

    Difference between ExtractIcon and ExtractAssociatedIcon? Need to extract icon of specific size

    Extract high resolution icon or thumbnail for file

    Updated:

    I accidentally found such a post, probably by specifying the width/height, remove LR_DEFAULTSIZE. Also, you must call DestroyIcon after DrawIconEx otherwise you get resource leak. Either that or create the HICON on heap so that it's created only once.