Reputation: 4938
I'm trying to get an icon (as HICON) for any file to draw it exactly in the same manner as Explorer does. It means that if I have a path to an exe file, I should draw its default icon. If I have a link (.lnk) file, I have to draw an icon, assigned to the link, if any, and its target file icon otherwise.
Unfortunately, SHGetFileInfo() which is supposed to return the icon, doesn't work for several link files. Here is the code:
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);
The first file is rendered as this (CORRECT):
The second one is rendered as this (INCORRECT):
But Explorer renders it as this:
I have tried SHGFI_SMALLICON modifier but to no avail. How to obtain icons for both cases? Or maybe is there another function to accomplish this?
UPDATE
The problem is that the application is x86 and the OS is x64. Is there a way to fix it not branching into 2 versions?
Upvotes: 2
Views: 1182
Reputation: 3317
The problem you have is known problem of 32 bit apps working on 64 bit Windows. System lnk parser works incorrectly with lnk files pointing to files in Program Files directory. Instead of Program Files parser extract Program Files x86 path. That why your trying of extraction icon or extraction target name fails. The only right solution is create 64 bit version of your app for 64 bit OS and 32 bit version for 32 bit OS. Also you can use hack - reading and parsing of lnk files with your own code. Some details: JclShell.ShellLinkResolve gets wrong data Another "hack" - using additional 64 bit process for reading icons of lnk files, but IMHO it is a bad way.
Upvotes: 2
Reputation: 597670
If you really want to get the same behavior as Explorer, then you need to use the same Shell interfaces that Explorer uses, such as IExtractIcon
and IExtractImage
:
There are two ways to retrieve an object's icon. The simplest way is to call SHGetFileInfo. However, this approach is inflexible and may be slow. A more flexible and efficient way to retrieve an item's icon is to use IExtractIcon. The Shell uses IExtractIcon to retrieve icons when it displays the contents of a folder. To use IExtractIcon to retrieve an object's icon, do the following:
- Get a pointer to the IShellFolder interface of the folder that contains the object.
- Call IShellFolder::GetUIObjectOf with the pointer to an item identifier list (PIDL) of the object and the interface ID of IExtractIcon (IID_IExtractIcon). The folder creates an object to handle the icon extraction, and returns the object's IExtractIcon interface pointer.
- Call IExtractIcon::GetIconLocation to retrieve the icon's location.
- Call IExtractIcon::Extract to retrieve the icon's handle.
Use IExtractImage if you are implementing a view of namespace objects, and want to display thumbnail images. You can use a Shell folder's IShellFolder::GetUIObjectOf method to bind to its IExtractImage interface.
Remember that everything in the Shell is represented by PIDLs and are thus accessible via IShellFolder
and related interfaces.
You can translate a path and filename into an absolute PIDL using SHParseDisplayName()
, and then get the IShellFolder
of its parent folder using SHBindToParent()
.
Upvotes: 3