SHGetFileInfo为OneDrive文件夹返回错误的图标

时间:2017-02-25 18:01:28

标签: c++ winapi

我正在尝试使用SHGetFileInfo来获取文件和文件夹的图标。我注意到它在一种情况下不起作用:我的用户配置文件夹中的OneDrive文件夹。在这种情况下,对SHGetFileInfo的调用成功,但我得到了可执行文件的默认图标:

What I get instead of the OneDrive icon

以下是我如何调用该函数:

HIMAGELIST imageList;
SHFILEINFO shfi;

// Get the path to the OneDrive folder
LPTSTR src = _T("%USERPROFILE%\\OneDrive");
TCHAR dest[MAX_PATH];
ExpandEnvironmentStrings(src, dest, MAX_PATH);

// Load the image from the OneDrive folder
ZeroMemory(&shfi, sizeof(shfi));
imageList = (HIMAGELIST)SHGetFileInfo(
    dest,
    FILE_ATTRIBUTE_NORMAL,
    &shfi,
    sizeof(shfi),
    SHGFI_ICON | SHGFI_SYSICONINDEX);

以下是我绘制图标的方式:

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    ImageList_Draw(imageList, shfi.iIcon, hdc, 0, 0, ILD_NORMAL);
    EndPaint(hwnd, &ps);
}
break;

我在pastebin上为应用做了一个简短的自包含示例:http://pastebin.com/bdTvEQT5

在这个简短的自包含示例中,为简洁起见省略了任何错误检查代码。此外,出于示例的目的,我已经对文件夹的位置进行了硬编码。在我的真实应用程序中,我在枚举文件系统中的文件夹,这就是我如何获取OneDrive文件夹的位置。图标提取代码需要使用枚举的任何文件夹。

我做错了什么?

1 个答案:

答案 0 :(得分:0)

您的代码在Windows 10(15031)上适用于我。您永远不会使用shfi.hIcon,因此您不需要SHGFI_ICON,但它不会阻止它在我的情况下工作,但您正在泄漏图标。

%USERPROFILE%\OneDrive可能不是OneDrive文件夹的位置。当有可以告诉你路径的shell函数时,永远不要使用环境变量!

您不能假设首先存在OneDrive文件夹。如果未使用OneDrive,则Windows 8.1上不存在该文件夹,因此您应该处理来自SHGetFileInfo的失败!

我建议您将代码更改为以下内容:

PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetKnownFolderIDList(FOLDERID_SkyDrive, 0, NULL, &pidl); // Note: Change the flags if you want to create the folder if it does not exist
if (SUCCEEDED(hr))
{
    imageList = (HIMAGELIST) SHGetFileInfo((LPTSTR) pidl, 0, &shfi, sizeof(shfi), SHGFI_PIDL | SHGFI_SYSICONINDEX);
    ILFree(pidl);
}
if (!imageList)
{
    // Fall back to a plain folder icon
    imageList = (HIMAGELIST) SHGetFileInfo(TEXT("DoesNotMatter"), FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
}

我无法解释您所看到的结果,缺乏错误检查肯定无济于事。我猜这是其中之一:

  • 在致电CoInitialize之前,您没有致电OleInitialize / SHGetFileInfo。 MSDN确实说你必须这样,但它似乎不太可能返回正确的图像列表但是错误的图标索引。
  • OneDrive已知文件夹注册已损坏。
  • shell系统映像列表已损坏。