I use SHGetFileInfo(SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES) to
obtain the icon associated with a file extension. I need it for the
drag'n'drop icons on Vista, so I use it with
SHGetImageList(SHIL_JUMBO) and IImageList::GetIcon().
Unfortunately it seems that this only returns nice 256x256 icons for
standard entities, and for most 3rd party file-extensions (such as
PDF,RAR) it returns a 256x256 icon with a puny 16x16 version of the
file-icon painted in the upper-left corner. I did expect it to at
least use the 32x32 version (or largest possible) and scale it onto
the 256x256 canvas.
The problem is that I have no way to detect when this happens! I want
an icon that fills the entire 256x256 canvas.
SHGetImageList(SHIL_EXTRALARGE) does return a proper icon that I can
rescale, but I need to know when the jumbo icons are actually
available, because I prefer to display the them.
Anyone managed to extract custom 256x256 icons successfully or know
how to detect when it fails to provide a proper result? Does this
really mean I have to go through the Registry and do everything
SHGetFileInfo() and SHGetImageList() was supposed to do for me?
regards
bjarke
What I am seeing is that the jumbo image list scales non-jumbo icons to 48x48. Clearly the list-view has access to the original icon size because it centres the image. IImageList2::GetOriginalSize looks like just the ticket, but sadly it just returns E_NOTIMPL. I stepped through it in the debugger and it seems to be failing because of an incorrectly constructed vtable :-(
--
Jim Barry, MVP (Windows SDK)
Scratch that - I was misled by bogus info in the debugger. What is actually happening is that the system image list in Vista is a special kind of "layered" image list that combines a foreground and background image list. This layered image list does not implement IImageList2::GetOriginalSize. The background image is the frame drawn around icons that are smaller than the image list size.
When the image list is asked to draw an item, if the requested width is equal or smaller than the image, it simply draws the foreground image at the requested size. Otherwise, the background image is drawn and then the foreground image is drawn on top, in the middle.
However, the code behind IImageList::GetIcon passes zero for the dimensions, so the layered image list just draws the foreground image in the top left corner. Oops.
I would suggest using IImageList::Draw to create your own drag image. 256x256 is a bit big anyway - the shell uses 96x96.
Thanks for the reply Jim.
I should probably have mentioned that I'm using this from a Shell
Extension IExtractIcon implementation, where the shell asks for a
256x256 icon as part of the drag'n'drop operation. It needs to return
a HICON.
The Shell's own DefView implementation is generating icons correctly,
so I was thinking there was an simple way to get these from a generic
filename/ext via SHIL_JUMBO.
> I would suggest using IImageList::Draw to create your
> own drag image. 256x256 is a bit big anyway - the shell uses 96x96.
But how? And I don't think 96x96 is what is used as source during
Explorer drag'n'drop. If I return icons using SHIL_EXTRALARGE the up-
scaled drag'n'drop image looks slightly distorted for file-extensions
where jumbo would otherwise be available, so the shell clearly uses
256x256 when it can.
regards
bjarke
You can create colour and mask bitmaps, draw the image into them, and pass them to CreateIconIndirect. The colour bitmap would be drawn with ILD_TRANSPARENT and the mask bitmap with ILD_MASK. However, the handling of alpha channels is rather tricky, and even the IImageList::GetIcon implementation that professes to do it "from the outside" actually uses private image list interfaces :-(
I tried to find alternative ways to extract an icon, but all avenues seem to be blocked - IImageList::DragBegin and IImageList::Copy both return E_NOTIMPL.
> I should probably have mentioned that I'm using this from a Shell
> Extension IExtractIcon implementation, where the shell asks for a
> 256x256 icon as part of the drag'n'drop operation. It needs to return
> a HICON.
Vista's drag-drop helper object uses the thumbnailing engine to generate the drag image representing the items in the data object. In addition to an icon handler, perhaps you can implement a thumbnail provider that obtains and returns the thumbnails of the files you want to represent. See IThumbnailProvider, IThumbnailCache, CLSID_LocalThumbnailCache etc.
Hi Bjarke, I think I figured out what you are seeing here. SHGetFileInfo gives you a 256x256 icon with an "extra large" 48x48 image drawn in the top left corner. Then, Explorer shrinks the icon down to 96x96 for the drag image. The end result is a puny 18x18 icon in the top left of the drag image.
Did you try implementing a thumbnail provider as I suggested?
Yes. Makes sense.
> Did you try implementing a thumbnail provider as I suggested?
No, not really. My Shell Extension is not DefView based, and not able
to hand out IShellItem nor a filename to a real file. Maybe the Vista
Icon Cache does hold the answer, but it's sad this makes SHIL_JUMBO
kind of useless.
Anyway, thanks for the suggestion and time spent Jim. See you on the
WTL list.
regards
bjarke
Out of curiosity, I delved deeper into this. The more I look, the worse it gets. I discovered that the system icon handler doesn't support anything other than the standard small/large icon sizes. So even if you go via GetUIObjectOf to obtain an icon for a file, you cannot get a 256x256 icon. You would indeed have to completely replicate the shell's mechanism for locating icons :-(
Hi Bjarke, I think I may have figured out a solution to your problem. You can obtain an IShellItem interface for a non-existent file by calling SHCreateItemFromParsingName, passing a bind context with the STGM_CREATE mode set in the bind options. You can then call this shell item's IShellItemImageFactory::GetImage method to obtain a bitmap, which you can return from your implementation of IThumbnailProvider::GetThumbnail. Let me know whether it works :-)