Reading and Saving .ICO files and resources in VB
Download the cFileIcon class and demonstration project (37kb)
VB allows you to load an save .ICO files through the LoadPicture and SavePicture
methods, which are implemented in the StdPicture object. Unfortunately, this object
was really written for 16 bit Windows and doesn't understand Win32 icons at all. A .ICO resource
can contain many different images, at different colour depths.
The Internet Explorer 5 main icon
Windows uses the different sizes and colours as required in the system. There is no need to
stick to the standard sizes and colour depths as shown in the image either. Icons can be defined
at any size (up to a maximum of 128x128 pixels) and at any colour depth.
When you load an icon using the StdPicture object, you only ever get one size: 32x32. It doesn't
matter whether there isn't actually a 32x32 image in the .ICO resource, it stretches it as
required (usually with horrible consequences). The loaded colour depth is also the always the
default system icon colour depth.
There are workarounds for the StdPicture object problem - see my tip
Create a VB Picture from an API Icon Handle and the article Get Icons for any File
Type for examples. However, all API methods for loading icons are restricted to only loading
icon resources at the system default icon colour depth. On a system with "Show Icons Using All
Available Colours" set, you can never load a 16 colour icon if a higher colour depth is available using the API methods.
You can do this if you load the icon natively, though. This article briefly describes the
.ICO file format and provides a class which reads icons from .ICO files and Executables and
also writes to standard .ICO files. This class is also used in the
Icon Explorer project.
The .ICO File Format
ICON files have a defined file format which is described for C programmers in
an MSDN article by John Hornick. (Download this icon
- there is a picture of him, or at least someone, stored in it at the 118x128 version!).
A .ICO file contains the following information:
If you are interested in why there are two images in an icon, check out the
Transparent Sprite Library article code, which describes the theory of using a mask and an
image to implement graphics with transparent areas.
- A header indicating the type of resource and the number of icons within it.
- A series of directory entries providing information about the size and colour depth of
the icon, and a pointer to where the icon data starts below it within the file.
- A series of icon data structures, containing a BITMAPINFOHEADER structure defining the
DIB section used for the icon's image, the palette table (if one is required for the image)
and then two byte arrays. The first contains the raw icon image bits in the appropriate DIB
format and the second contains the image bits for the mask as a monochrome DIB.
In more detail, the structures to implement the above look like this:
Private Type ICONDIR
- The icon header:
idReserved As Integer '// Reserved
idType As Integer '// resource type ( = IMAGE_ICON = 1 for icons)
idCount As Integer '// how many images?
' idEntries() as ICONDIRENTRY array follows.
' After that, the icon bits.
The icon directory entry:
Private Type ICONDIRENTRY
bWidth As Byte '// Width of the image
bHeight As Byte '// Height of the image (times 2)
bColorCount As Byte '// Number of colors in image (0 if >=8bpp)
bReserved As Byte '// Reserved
wPlanes As Integer '// Color Planes
wBitCount As Integer '// Bits per pixel
dwBytesInRes As Long '// how many bytes in this resource?
dwImageOffset As Long '// where in the file is this image
The icon data structures. These cannot be represented as a straightforward type in VB
because the members of the type depend on how many colours are in the image and the size
of the icon bitmaps. If you were to write this as a real VB structure, you would
have to create at least four different types to accomodate the different
number of colour entries. I have worked around this in my icon class by storing this type
as a raw byte array and interpreting it as required. However, in pseudo-VB code, the type looks
Private Type ICONIMAGE
' The DIB Header:
icHeader As BITMAPINFOHEADER
' The Colour Table If Required:
If (NumberOfColours<=256 Colours) Then
icColors(1 to NumberOfColours) as RGBQUAD
' The Image Bytes:
icXOR(0 to (Width * Bits Per Pixel,aligned to a Long Boundary) * Height) As Byte
' The Mask Bytes
icAND(0 to (Width/8 aligned to a Long Boundary) * Height) As Byte
Icons In Executable Files
In an executable file, icons are stored in a similar structure, but there are two differences.
Firstly, the structures are packed on 2 byte boundaries rather than the normal 4 bytes, and
secondly the dwImageOffset member of the ICONDIRENTRY type is replaced with
an integer icon ID number. Because the icons are packed on 2 bytes, you cannot write a VB
structure which can be directly used to read the format, because VB automatically aligns all its
structures on 4 byte boundaries. Instead you have to read all the data in bytes and then
copy it into the appropriate structure.
Translating it to VB
To say this wasn't particular easy to translate to VB would be understating the case a bit.
I'd describe it here, but it would go on for ever! Instead, download the demonstration
project and pick through that. If you'd like to know more about the DIB bitmap format
used to store the images, there are two resources here:
The result is a cFileIcon class. The interface to this class is as follows:
Public Function AddImage(ByVal nWidth As Long, ByVal nHeight As Long, ByVal nColourCount As Long) As Long
Adds a icon to the class with specified width,height and number of colours and returns
the index of the new icon. The newly added icon is initialised to be completely black.
To modify the icon, you need to add something to the class to set the DIB bits of the
XOR (Image) and AND (Mask) DIBs.
Public Sub CloneTo(ByRef cThis As cFileIcon)
Makes a copy of one cFileIcon class to another. The cloned class (cThis) is
a completely independent copy.
Public Sub DrawIconImage( _
ByVal lHDC As Long, _
ByVal nIndex As Long, _
Optional ByVal eType As ECFIImageConstants = ecfiImage, _
Optional ByVal X As Long = 0, Optional ByVal Y As Long = 0, _
Optional ByVal lWidth As Long = 0, Optional ByVal lHeight As Long = 0, _
Optional ByVal eRasterOp As RasterOpConstants = vbSrcCopy _
Draws either the Image or the Mask portion of the image to the specified DC, optionally
at a specified X,Y position, stretching to a given width and height and drawn using
one of the RasterOpConstants.
Public Property Get Filename() As String
Returns the filename of the .ICO file or the Executable from which the icon was loaded.
Public Property Get ImageColourCount(ByVal nIndex As Long) As Long
Returns the number of colours in the icon at 1 based index nIndex.
Public Property Get ImageCount() As Long
Returns the number of icon resources within the currently loaded icon.
Public Property Get ImageHeight(ByVal nIndex As Long) As Long
Returns the height in pixels of the icon at the 1 based index nIndex.
Public Property Get ImageSize(ByVal nIndex As Long) As Long
Returns the number of bytes of the icon at the 1 based index nIndex.
Public Property Get ImageWidth(ByVal nIndex As Long) As Long
Returns the width in pixels of the icon at the 1 based index nIndex.
Public Function LoadIcon(ByVal sFile As String) As Boolean
Loads an icon from a .ICO file. Returns True if it succeeded, or False otherwise.
An error will be raised if there is a problem loading the icon.
Public Function LoadIconFromEXE( _
ByVal sFile As String, _
Optional ByVal lpID As Long = 0, _
Optional ByVal lpName As String = "" _
) As Boolean
Loads an icon from an executable file (e.g. EXE, OCX, DLL). Specify lpID
if the resource has an integer ID, otherwise specify lpName.
Returns True if it succeeded, or False otherwise.
An error will be raised if there is a problem loading the icon.
Public Function RemoveImage(ByVal nIndex As Long) As Long
Removes the icon resource ID at the 1 based index nIndex.
Public Property Get ResourceID() As Variant
Returns the resource ID that the current icon was extracted from, or Empty if the icon was loaded from
Public Function SaveIcon(Optional ByVal sFileName As String = "") As Boolean
Saves the current icon resources to a .ICO file. If sFileName is not specified, the
file that the icon was loaded from will be used. Returns True if successful, or False
otherwise. An error will be raised if there is any problem saving the icon.
Back to top
Back to Source Code