SHGetFileInfo() 返回错误的 HICON

Posted

技术标签:

【中文标题】SHGetFileInfo() 返回错误的 HICON【英文标题】:SHGetFileInfo() returns wrong HICON 【发布时间】:2014-11-10 12:06:29 【问题描述】:

我正在尝试为任何文件获取一个图标(作为 HICON),以便以与 Explorer 完全相同的方式绘制它。这意味着如果我有一个 exe 文件的路径,我应该绘制它的默认图标。如果我有一个链接 (.lnk) 文件,我必须绘制一个图标,分配给链接(如果有),否则它的目标文件图标。

不幸的是,应该返回图标的 SHGetFileInfo() 不适用于多个链接文件。代码如下:

SHFILEINFO fi = 0;
::SHGetFileInfo(L"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Windows Mobile Device Center.lnk",
//::SHGetFileInfo(L"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\TortoiseGit\\TortoiseGitMerge.lnk",
0, &fi, sizeof(fi), SHGFI_ICON /*| SHGFI_SMALLICON*/);

CPaintDC dc(this);
dc.DrawIcon(0, 0, fi.hIcon);

第一个文件呈现如下(正确):

第二个被渲染成这样(不正确):

但 Explorer 将其呈现为:

我尝试了 SHGFI_SMALLICON 修饰符,但无济于事。如何获取两种情况的图标?或者是否有其他功能可以实现这一点?

更新

问题是应用程序是 x86 而操作系统是 x64。有没有办法解决它不分支到 2 个版本?

【问题讨论】:

你的应用是 32 位的,操作系统是 64 位的吗? @DenisAnisimov 是的,该应用程序是 32 位的,操作系统是 64 位的。 @DenisAnisimov 嗯...我已将平台更改为 x64,现在一切正常。它是错误还是记录在案的功能? 32/64 位应该没关系。 @JonathanPotter 但确实如此。就我更改平台而言,它开始呈现正确的图标。 【参考方案1】:

如果你真的想得到和 Explorer 一样的行为,那么你需要使用和 Explorer 一样的 Shell 接口,比如IExtractIconIExtractImage

有两种方法可以检索对象的图标。最简单的方法是调用 SHGetFileInfo。然而,这种方法不灵活并且可能很慢。检索项目图标的更灵活和有效的方法是使用 IExtractIcon。 Shell 在显示文件夹的内容时使用 IExtractIcon 检索图标。要使用 IExtractIcon 检索对象的图标,请执行以下操作:

    获取指向包含该对象的文件夹的 IShellFolder 接口的指针。 使用指向对象的项标识符列表 (PIDL) 的指针和 IExtractIcon (IID_IExtractIcon) 的接口 ID 调用 IShellFolder::GetUIObjectOf。该文件夹创建一个对象来处理图标提取,并返回该对象的 IExtractIcon 接口指针。 调用 IExtractIcon::GetIconLocation 以检索图标的位置。 调用 IExtractIcon::Extract 以检索图标的句柄。

如果您正在实现命名空间对象的视图,并且想要显示缩略图图像,请使用 IExtractImage。您可以使用 Shell 文件夹的 IShellFolder::GetUIObjectOf 方法绑定到其 IExtractImage 接口。

请记住,Shell 中的所有内容都由 PIDL 表示,因此可以通过 IShellFolder 和相关接口访问。

您可以使用SHParseDisplayName() 将路径和文件名转换为绝对PIDL,然后使用SHBindToParent() 获取其父文件夹的IShellFolder

【讨论】:

【参考方案2】:

您遇到的问题是 32 位应用程序在 64 位 Windows 上运行的已知问题。系统 lnk 解析器无法正确处理指向 Program Files 目录中文件的 lnk 文件。而不是 Program Files 解析器提取 Program Files x86 路径。这就是您尝试提取图标或提取目标名称失败的原因。唯一正确的解决方案是为 64 位操作系统创建 64 位版本的应用程序,为 32 位操作系统创建 32 位版本。您也可以使用 hack - 使用您自己的代码读取和解析 lnk 文件。一些细节:JclShell.ShellLinkResolve gets wrong data 另一个“hack” - 使用额外的 64 位进程来读取 lnk 文件的图标,但恕我直言,这是一个不好的方法。

【讨论】:

我在 Shell API 中看到了“程序文件 [x86]”的混乱,但没有意识到他们对图标使用了相同的代码。谢谢。

以上是关于SHGetFileInfo() 返回错误的 HICON的主要内容,如果未能解决你的问题,请参考以下文章

SHGetFileInfo函数详解

HiC数据分析之-HiC-Pro

MFC文件之SHGetFileInfo函数与SHFILEINFO结构体

使用 SHGetFileInfo 获取 exe 图标失败

HiC

如何使用Shell32.SHGetFileInfo在Windows 7上获取文件夹图标