Click here to Skip to main content
Click here to Skip to main content

Access multiple icons in a single icon file

, 23 Feb 2004
Rate this:
Please Sign up or sign in to vote.
A class to help you access the images in an ICO file (VB & C#)
<!-- Article image -->

Introduction



GDI+ is really great for many things but there are a few missing pieces. One of those missing pieces is the ability to view the different versions of an icon contained in a single ICO file. In this article I will show you how to access and utilize these different versions.

Because everyone likes this topic I have written the code in C# as well.  I figured I'd dust off my C# skills and recode the app.  This new C# version is pretty much the same as the VB version except it exposes more details inside the icon file; see the source code for details.

Icon Files

Very often, icon files contain multiple versions of the same image. Usually these files will contain small and large versions of the same image so you will not have to resize the image and risk loosing valuable resolution.

Limitations of "new Icon"

The new icon constructor in Visual Studio does not have good support for multiple image versions. While it is true that you can specify the height and width of the icon you are requesting, you have no way of knowing what to request (if you don't know what's in the file). To get around this limitation we have to look at the ico file directly and pull out the bits and bytes that we want to utilize. I have created a graph that will illustrate the internal layout of an icon file.

For reasons of space I only put two icons in the above sample but there can be as many as you want.

Icon Header
Icon Header holds the key to accessing the entire file. This six Byte block tells you how many icons are in the file you are accessing as well as the type of file you are accessing (0 for Bitmap and 1 for Icon).

Icon Entry
After we read the Icon Header we will know how many icons are in this icon file. We can then read that many Icon Entry blocks safely. Icon Entry blocks do not hold the image information, they hold the Offset and the Length of the image Bytes.

Opening the icon file

The easiest way is to create a FileStream and dump the file to a Byte array. You can then load the Byte array into a Stream object. The following code will demonstrate.

    Private Function readIcoFile(ByVal filename As String) As MemoryStream
        ' Open the file
        '
        Dim icoBinaryFile As New FileStream(filename, FileMode.Open, 
                                            FileAccess.Read)


        ' Create the byte array and read it in
        '
        Dim byteArray(icoBinaryFile.Length) As Byte
        icoBinaryFile.Read(byteArray, 0, icoBinaryFile.Length)
        icoBinaryFile.Close()


        ' Load the stream with the bytearray
        '
        icoStream = New MemoryStream(byteArray)
        icoStream.Seek(0, SeekOrigin.Begin)


        ' Debug, these values should be the same!
        '
        Console.WriteLine("Number of bytes: " & byteArray.Length)
        Console.WriteLine("Length of Stream: " & icoStream.Length)
    End Function

Reading the data

The best way I have found to do this is create classes. The first class would be the icon header class and it would contain only definitions for the elements it reads and a new procedure that will read the information from the stream.

    Private Class iconHeader
        Public Reserved As Short      ' Always 0
        Public Type As Short          ' 0=Bitmap, 1=Icon
        Public Count As Short         ' Number of icons


        '------------------------------------------
        '     Sub: New
        ' Purpose: Read the six byte header from
        '          the raw ICO file
        '    Note: Short or Int16 are 2 bytes each
        '
        Public Sub New()
            Dim icoFile As New BinaryReader(icoStream)

            Reserved = icoFile.ReadInt16
            Type = icoFile.ReadInt16
            Count = icoFile.ReadInt16
        End Sub
    End Class

The next step is pretty much the same, but with the Icon Entry data.

    Private Class iconEntry
        Public Width As Byte          ' Width, in pixels, of the image
        Public Height As Byte         ' Height, in pixels, of the image
        Public ColorCount As Byte     ' Number of colors in image(0 if >=8bpp)
        Public Reserved As Byte       ' Reserved ( must be 0)
        Public Planes As Short        ' Color Planes
        Public BitCount As Short      ' Bits per pixel
        Public BytesInRes As Integer  ' How many bytes in this resource?
        Public ImageOffset As Integer ' Where in the file is this image?

        '------------------------------------------
        '     Sub: New
        ' Purpose: Read the sixteen byte header from
        '          the raw ICO file
        '    Note: Byte is 1 byte
        '          Short or Int16 are 2 bytes
        '          Integer or Int32 are 4 bytes
        '
        Public Sub New(ByVal Index As Integer)
            Dim icoFile As New BinaryReader(icoStream)

            Width = icoFile.ReadByte
            Height = icoFile.ReadByte
            ColorCount = icoFile.ReadByte
            Reserved = icoFile.ReadByte
            Planes = icoFile.ReadInt16
            BitCount = icoFile.ReadInt16
            BytesInRes = icoFile.ReadInt32
            ImageOffset = icoFile.ReadInt32
        End Sub
    End Class

You now have all the Offsets, Sizes, Color Information and Lengths of the images.

Building an image

Ok, what we do here is actually build an icon using the information we extracted from the headers and entries. We do this by creating another stream and using a BinaryWriter to fill in the data. Here is an example:

    Private Function buildIcon(ByVal index As Integer) As Icon
        Dim thisIcon As iconEntry = icons(index)

        ' Allocate the space for the icons byteArray
        Dim icoByteArray(thisIcon.BytesInRes) As Byte

        ' Create the stream
        Dim newIcon As New MemoryStream
        Dim writer As New BinaryWriter(newIcon)

        ' Only one icon in this file
        Dim newCount As Short = 1


        ' Six Bytes + Sixteen Bytes is the new offset
        Dim newOffset As Integer = 22

        Console.WriteLine("Icon Index: " & index & ", 
                          Offset: " & thisIcon.ImageOffset)

        ' Write the file
        With writer
            .Write(icoHeader.Reserved)
            .Write(icoHeader.Type)
            .Write(newCount)
            Console.WriteLine("Header written: " & newIcon.Position)

            .Write(thisIcon.Width)
            .Write(thisIcon.Height)
            .Write(thisIcon.ColorCount)
            .Write(thisIcon.Reserved)
            .Write(thisIcon.Planes)
            .Write(thisIcon.BitCount)
            .Write(thisIcon.BytesInRes)
            .Write(newOffset)
            Console.WriteLine("Image Header written: " & newIcon.Position)

            ' Read the icon from the stream
            icoStream.Seek(thisIcon.ImageOffset, SeekOrigin.Begin)
            icoStream.Read(icoByteArray, 0, thisIcon.BytesInRes)

            ' Write it out
            .Write(icoByteArray)
            .Flush()
            Console.WriteLine("Image written: " & newIcon.Position)
        End With

        ' Move to the start
        newIcon.Seek(0, SeekOrigin.Begin)

        Dim thisImage As Icon = New Icon(newIcon, thisIcon.Width, 
                                         thisIcon.Height)
        writer.Close()

        Return thisImage
    End Function

Notice, we are just writing back pretty much a copy of the data we read (with a few changes). The changes we made where:

' We only have one icon in this file 
Dim newCount As Short = 1 
' We are moving the image offset so it falls right after the headers 
Dim newOffset As Integer = 22 

The class to wrap all this

I created a class that I call MultiIcon it contains the following properties and methods:

  • count
    The number of icons in an ico file
  • sizes
    An array of Size with the sizes of the icons in the ICO
  • image
    Returns an image for a specific icon
  • findIcon
    Returns an image but you can specify if you want the largest or smallest image

Examples

Public thisIcon As multiIcon(filename)

This will create a new instance of the class.

PictureBox1.Image = thisIcon.image(ComboBox1.SelectedIndex).ToBitmap

This will load a icon into a picture box.

PictureBox1.Image = thisIcon.findIcon(
                                    multiIcon.iconCriteria.Largest).ToBitmap()
PictureBox1.Image = thisIcon.findIcon(
                                   multiIcon.iconCriteria.Smallest).ToBitmap()

This will load a specific version of an icon into a picturebox.

        Dim size As Size
        For Each size In thisIcon.sizes
            ComboBox1.Items.Add(size.ToString)
        Next

This will list all the sizes of the icons currently available.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Matthew Hazlett
Web Developer
United States United States
I started programming for fun when I was about 10 on an Franklin Ace 1000.
 
I still do it just for fun but it has gotten me a few jobs over the years. More then I can say for my Microsoft Certifications. Smile | :)
 
The way I learned was by example, now its time to give back to the next generation of coders.
 


Comments and Discussions

 
SuggestionAwesomest article PinmemberDennisKuhn8-Sep-11 11:46 
GeneralAccess multiple icons in a single icon file Pinmemberfedecodeproject10-Mar-11 10:41 
GeneralDoes not work in Windows 7 with 256x256 colors Pinmembererexa22-Feb-11 9:11 
Questionhow do i load an icon from a remote location such as a favicon.ico - http://www.codeproject.com/favicon.ico Pinmemberernster26-Jun-09 0:33 
AnswerRe: how do i load an icon from a remote location such as a favicon.ico - http://www.codeproject.com/favicon.ico PinmemberEli Gazit19-Jul-09 19:25 
General256x256 PNG icon support? [modified] Pinmembersmashly6617-Jul-08 16:07 
QuestionHow to properly scale a System.Drawing.Icon Pinmemberlogan133724-Oct-07 15:28 
AnswerRe: How to properly scale a System.Drawing.Icon PinmemberMatthew Hazlett24-Oct-07 19:34 
GeneralRe: How to properly scale a System.Drawing.Icon Pinmemberlogan133725-Oct-07 3:50 
QuestionHow-to use same to extract from a file/exe? Pinmemberhandheldmaster2-Feb-07 9:50 
Questioncan I use the loaded icon as a windows icon handle Pinmembersrinivas vaithianathan23-Jan-06 14:34 
AnswerRe: can I use the loaded icon as a windows icon handle PinmemberMatthew Hazlett23-Jan-06 14:39 
GeneralRe: can I use the loaded icon as a windows icon handle Pinmembersrinivas vaithianathan24-Jan-06 7:36 
AnswerRe: can I use the loaded icon as a windows icon handle PinmemberQuerulant15-Aug-06 1:11 
GeneralConvert PNG to ICO Pinmemberjgallen231-Nov-05 17:33 
GeneralMore than one icon PinmemberTsahiM28-Jun-04 8:51 
GeneralAccessing icon libraries Pinmembermikestu2-Mar-04 2:56 
GeneralRe: Accessing icon libraries PinmemberJoão Rezende14-Apr-05 10:17 
GeneralRe: Accessing icon libraries PinmemberUnruled Boy10-Jul-05 16:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 24 Feb 2004
Article Copyright 2004 by Matthew Hazlett
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid