使用图标处理程序 shell 扩展加载时文件图标模糊

File icons blurry when loaded with an icon handler shell extension

我有一个带有图标处理程序的 shell 扩展,它根据文件内容将文件类型的图标设置为绿色或红色图标。这些图标似乎可以正常工作,只是在使用大图标时它们真的很模糊,就好像它们是从非常小的尺寸放大的一样。图标 .ico 文件包含从 256x256 到 16x16 的所有图像尺寸。

我正在使用一个非常基本的图标处理程序,但可能仍然存在缓存或其他问题。我怎样才能确保图标加载正确?

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;
}

使用 DebugView 记录时,图标处理程序似乎要求适当的大小:

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

编辑: 根据@Anders,如果我检查用 LoadImage 加载的图像的大小,它似乎也是正确的:

*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

我以前看过这种信息documents。我不能保证现在这个信息是准确的。

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.

好像IExtract IconA:: Extract只能提取标准尺寸的图标。

另一方面,参考陈峰的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).

因此,如果您需要提取自定义尺寸的图标(即不同于系统 "small" 和 "large" 尺寸的图标),那么您需要做更多的工作。

调用 SHGetImageList 函数,这是另一个 shell 辅助函数,但它会检索包含图标的 shell 图像列表。它为您提供了更多图标大小选项:SHIL_SMALL(通常为 16x16)、SHIL_LARGE(通常为 32x32)、SHIL_EXTRALARGE(通常为 48x48)和 SHIL_JUMBO(通常为 256x256—仅适用于 Vista 及更高版本)。因此,如果您请求 SHIL_EXTRALARGE,您将获得所需的 48x48 图标。

您在这里仍然需要 SHGetFileInfo 函数,但这次是检索 shell 图像列表中所需图标的索引。使用 SHGFI_SYSICONINDEX 选项检索它。

完全未经测试的示例代码,从未被编译器触及:

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;
}

更多详情请参考:

Extract high resolution icon or thumbnail for file

更新:

无意中发现了这样一个,大概是指定了width/height,去掉了LR_DEFAULTSIZE。此外,您必须在 DrawIconEx 之后调用 DestroyIcon 否则会导致资源泄漏。或者在堆上创建 HICON 以便它只创建一次。