Extracting Icons

After trying out RocketDock, XWindows Dock, and Object Dock, and wanting to get a refresher of latest WPF/C# in Visual Studio 2013, I started building a little dock. Naturally, nothing is ever easy, and I spent the last 2-3 days just learning how to extract icons from running applications in a way that didn’t suck.

The screenshots below attempt to render large (128px) icons in an automated way.

All of the techniques can be performed in C# via interop. Some are already managed APIs.

Shell32.dll ExtractIconEx() and CreateBitmapSourceFromHIcon()

Very easy to use. Finds a lot of icons. But only grabs 32px icons.

.NET System.Drawing.Icon.ExtractAssociatedIcon()

Probably simplest method here. Grabs only one icon, and is limited to 32px icons.

Comctl32.dll, Shell32.dll Using SHGetImageList() and ImageList_GetIcon()

If you request Jumbo, you do get some high res icons, but you also get high-res icons with low-res 32px icons embedded in the upper-left corners. The resulting icon/bitmap claims it is 256×256. This approach lies about the resolution of the original image.

 

WindowsAPICodePack ShellFile.FromFilePath()

This C# API makes an otherwise complex method very easy. It automates a lot of heavy lifting around IShellFile interops and even generates WPF friendly ImageSources for you.

It suffers from a similar issue that native Shell functions have. ShellFile will lie about the resolution of the icon by returning a low-resolution icon centered in the middle of a nice high-resolution image. So yeah you get your 256×256 Jumbo icons, even if the application doesn’t have one.

User32.dll PrivateExtractIcons() with size = 0

This helpful function tells you real icon sizes, and chooses the larges icon sizes where possible. But, it doesn’t always work. It fails to return the highest resolution icons for some applications like Spotify, Chrome, and Onenote, but works great for a few others (Explorer and Visual Studio).  When returning low-resolution icons, it does a better job than the previous methods by providing 48px icons instead of the typical 32px.

User32.dll PrivateExtractIcons(Desired Size = 256)

Always returns the desired size images, even if the application doesn’t contain such icons. This means it stretches the original icons and then lies about it.

Picks up more high-res icons than Desired Size = 0. This is an easy way to get the best-quality icons available, but you won’t be able to tell whether an icon was upsampled from 48px or is a true 256px. A combination of size=0 and size=256 may be just what the doctor ordered.

Kernel32.lib, User32.dll EnumResourceNames(RT_GROUP_ICON) and LoadImage(IMAGE_ICON, Desired Size = 256)

A slightly weird approach, find icon-groups and then use LoadImage() to load them in my name (“#32512”, “IDR_MAINFRAME”, etc).

Returns all 256px native icons, but it upsamples low-res icons and passes them off as 256px icons, making it harder to identify them.

Kernel32.lib, User32.dll LookupIconIdFromDirectoryEx(Desired Size = 256), LoadResource(), and CreateIconFromResourceEx()

My current approach.

The slightly involved manual approach. Not as complex as IShellFolder and IExtractFile. Quite straight forward and well documented. Rummage through RT_GROUP_ICON entities, picking out the largest icons, then manually extracting and converting them into HIcons and then ImageSources.

The first RT_GROUP_ICON entry always seems to be the application’s icon, so you can skip the rest. Captures all 256px pngs and falls back to 48px icons. The best part: you know exactly what resolution the resulting images are.

Leave a Reply

Your email address will not be published.