【问题标题】:SHGetFileInfo returns wrong icon for OneDrive folderSHGetFileInfo 为 OneDrive 文件夹返回错误的图标
【发布时间】:2017-07-16 12:36:17
【问题描述】:

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

这是我调用函数的方式:

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 文件夹位置的方式。图标提取代码需要适用于任何枚举的文件夹。

我做错了什么?

【问题讨论】:

  • 您传递了(错误的)FILE_ATTRIBUTE_NORMAL 标志,但很幸运忘记设置SHGFI_USEFILEATTRIBUTES 标志,因此它被忽略了。 OneDrive 文件夹不是文件。您需要返回文档。
  • 你不检查返回值所以不知道API调用是否成功
  • 请不要在外部网站上托管您的代码。它属于复制/粘贴在实际问题中。
  • @IInspectable 我知道FILE_ATTRIBUTE_NORMAL 会被忽略,但我不知道该传递什么。我没有忘记使用SHGFI_USEFILEATTRIBUTES,我故意省略了它。如果您知道如何获取 OneDrive 文件夹的图标,请添加答案,我将非常乐意接受。我确实阅读了文档,如果您知道那是什么,请指出我错过的具体内容。
  • @RemyLebeau 代码的相关部分嵌入在这个问题中。我认为整个示例太大而无法放入问题中,因为我不希望问题因为太大而无法阅读而被忽略。

标签: c++ winapi


【解决方案1】:

您的代码在 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);
}

我无法解释您所看到的结果,而且缺少错误检查当然也无济于事。我猜它是其中之一:

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

【讨论】:

  • 感谢您抽出宝贵时间调查此事。说我的代码对你有用是一个巨大的帮助。问题实际上是 OneDrive 安装损坏。该文件夹仍然出现,但它没有工作。我重新安装了 OneDrive,现在图标显示而无需修改我的代码。我没有使用已知文件夹的原因是该代码旨在用于类似文件浏览器的应用程序中,因此我需要从文件系统枚举的文件夹中获取图标。我没有使用环境变量或对文件夹的位置做出假设。
  • @Ove:您的minimal reproducible example 确实对已知文件夹的位置做出了假设,该文件夹可以设置在其他地方。应该重写该代码以使用 shell 函数来查找其 PIDL。如果您的输入是枚举文件系统的结果,您可以忽略此建议,如在您的特定用例中。然而,这个问答对你的最终申请一无所知,这个问题可以改进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
相关资源
最近更新 更多