Click here to Skip to main content
12,457,938 members (50,494 online)
Click here to Skip to main content
Add your own
alternative version

Stats

119.6K views
5K downloads
102 bookmarked
Posted

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.



You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionUPDATE Pin
Matthew Hazlett14-Jun-16 10:29
memberMatthew Hazlett14-Jun-16 10:29 
GeneralMy vote of 5 Pin
Mojtaba Rezaeian22-Feb-16 22:37
memberMojtaba Rezaeian22-Feb-16 22:37 
QuestionRead icons from .EXE or .DLL, which is loaded byte array Pin
AcidUser8513-Aug-15 21:21
memberAcidUser8513-Aug-15 21:21 
SuggestionAwesomest article Pin
DennisKuhn8-Sep-11 11:46
memberDennisKuhn8-Sep-11 11:46 
GeneralAccess multiple icons in a single icon file Pin
fedecodeproject10-Mar-11 10:41
memberfedecodeproject10-Mar-11 10:41 
GeneralDoes not work in Windows 7 with 256x256 colors Pin
erexa22-Feb-11 9:11
membererexa22-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 Pin
ernster26-Jun-09 0:33
memberernster26-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 Pin
Eli Gazit19-Jul-09 19:25
memberEli Gazit19-Jul-09 19:25 
General256x256 PNG icon support? [modified] Pin
smashly6617-Jul-08 16:07
membersmashly6617-Jul-08 16:07 
QuestionHow to properly scale a System.Drawing.Icon Pin
logan133724-Oct-07 15:28
memberlogan133724-Oct-07 15:28 
AnswerRe: How to properly scale a System.Drawing.Icon Pin
Matthew Hazlett24-Oct-07 19:34
memberMatthew Hazlett24-Oct-07 19:34 
GeneralRe: How to properly scale a System.Drawing.Icon Pin
logan133725-Oct-07 3:50
memberlogan133725-Oct-07 3:50 
QuestionHow-to use same to extract from a file/exe? Pin
handheldmaster2-Feb-07 9:50
memberhandheldmaster2-Feb-07 9:50 
Questioncan I use the loaded icon as a windows icon handle Pin
srinivas vaithianathan23-Jan-06 14:34
membersrinivas vaithianathan23-Jan-06 14:34 
AnswerRe: can I use the loaded icon as a windows icon handle Pin
Matthew Hazlett23-Jan-06 14:39
memberMatthew Hazlett23-Jan-06 14:39 
GeneralRe: can I use the loaded icon as a windows icon handle Pin
srinivas vaithianathan24-Jan-06 7:36
membersrinivas vaithianathan24-Jan-06 7:36 
AnswerRe: can I use the loaded icon as a windows icon handle Pin
Querulant15-Aug-06 1:11
memberQuerulant15-Aug-06 1:11 
GeneralConvert PNG to ICO Pin
jgallen231-Nov-05 17:33
memberjgallen231-Nov-05 17:33 
GeneralMore than one icon Pin
TsahiM28-Jun-04 8:51
memberTsahiM28-Jun-04 8:51 
GeneralAccessing icon libraries Pin
mikestu2-Mar-04 2:56
membermikestu2-Mar-04 2:56 
GeneralRe: Accessing icon libraries Pin
João Rezende14-Apr-05 10:17
memberJoão Rezende14-Apr-05 10:17 
GeneralRe: Accessing icon libraries Pin
Unruled Boy10-Jul-05 16:22
memberUnruled Boy10-Jul-05 16:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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 | Terms of Use | Mobile
Web01 | 2.8.160826.1 | Last Updated 24 Feb 2004
Article Copyright 2004 by Matthew Hazlett
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid