(Sample Application running on Windows Technical Preview build 9860)
Introduction
Here I will introduce a .NET class library for extracting an icon from an EXE or DLL file. Though it's common to use ExtractIconEx()
Win32 API function for this purpose, it can extract only a few variations of an icon. Look at the screenshot above. This library enables us to extract all the variations and split it into separate objects.
I posted the first version of this article around six years ago. Honestly, I have been a bit ashamed of it lately. It discussed almost nothing, the code was dirty, buggy, inefficient, blah blah blah... So I rewrote the overall article, sample code and library itself. The significant updates are:
- Can load a 64-bit DLL/EXE from 32-bit process, and vice versa
- Recognizes huge icons used in Windows 10 (i.e. 768 x 768)
- More memory efficient
- No longer implements IDisposable
- Some utility methods moved to IconUtil class
Using the library
This library consists of two classes: IconExtractor
and IconUtil
. IconExtractor
is the main class of this library. It is associated to a file and enables us to extract icons from it. IconUtil
is a misc utility class.
This is a quite small and simple library. So the short example below covers all the methods and properties.
using System;
using System.Drawing;
using TsudaKageyu;
IconExtractor ie = new IconExtractor(@"D:\sample.exe");
string fileName = ie.FileName;
int iconCount = ie.Count;
Icon icon0 = ie.GetIcon(0);
Icon icon1 = ie.GetIcon(1);
Icon[] allIcons = ie.GetAllIcons();
Icon[] splitIcons = IconUtil.SplitIcon(icon0);
Bitmap bitmap = IconUtil.ToBitmap(splitIcon[1]);
int bitDepth = IconUtil.GetBitCount(splitIcon[2]);
How it works
The basic strategy used in this library is: Manipulate an .ico file in memory. As it's difficult and risky to manipulate the internal data of Icon
object directly (It's possible, but a kind of black magic). However, once coverted into an .ico file like following, its structure is the common knowledge among Windows developers.
Icon icon0 = new Icon(...);
MemoryStream ms = new MemoryStream();
icon0.Save(ms);
Icon icon1 = new Icon(ms);
Actually, an Icon 
object stores its own file image internally. So this library directly access to the internal buffer instead of saving to MemoryStream
.
.ico file structure
An .ico file is an archive of some pictures. It consists of the count of pictures, brief information on each picture, and the actual pictures. It has a quite straightforward format, so it's easy to manipulate it. The image below roughly illustrates the structre of an .ico file and how to split and merge it.
If you need more detailed information, refer to the official document of Microsoft.
Gathering the icon resource and build an .ico file
Creating an icon from resource is a similar process to merging icons.
The icons and other materials are embedded in the executable file in binary form. Those pieces cannot be loaded directly with .NET classes, because they are different from the managed resources that .NET Framework can handle. They should be loaded with Win32 API functions such as FindResource()
, LoadResource()
etc.
private byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name)
{
IntPtr hResInfo = NativeMethods.FindResource(hModule, name, type);
if (hResInfo == IntPtr.Zero)
throw new Win32Exception();
IntPtr hResData = NativeMethods.LoadResource(hModule, hResInfo);
if (hResData == IntPtr.Zero)
throw new Win32Exception();
IntPtr pResData = NativeMethods.LockResource(hResData);
if (pResData == IntPtr.Zero)
throw new Win32Exception();
uint size = NativeMethods.SizeofResource(hModule, hResInfo);
if (size == 0)
throw new Win32Exception();
byte[] buf = new byte[size];
Marshal.Copy(pResData, buf, 0, buf.Length);
return buf;
}
It looks compilcated, but has some historical reasons date back to 16-bit Windows. A well-known Microsoft blogger Raymond Chen discussed it here and here.
The binary data stored in the resource is in similar form to an .ico file, so we can easily convert the data to an .ico file.
History
- 11 Nov, 2014: Overall revision
- 10 Jun, 2008: Initial post