I've always had to use a program that does this stuff for me, but I'd like to just make my own program that does this. I've already got the individual bitmap images of the 3 sizes (16x16, 32x32 and 48x48) for the icon file and I usually find use some shareware program to create the icon file from the multiple bitmap images for me but I'm wondering if it's possible in .Net to create an icon file (*.ico) that contains multiple physical icons in it.
Currently using VS 2015 Enterprise on Win10 Enterprise x64.
You can draw multiple images to another one, once you have done that just use this sub:
Code:
Private Sub CreateIcon(ByVal bitmapName As String)
Try
Dim fi As New System.IO.FileInfo(bitmapName)
Dim bmp As New Bitmap(fi.FullName)
Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(fi.FullName.Replace(fi.Extension, ".ico"))
Icon.FromHandle(bmp.GetHicon).Save(sw.BaseStream)
sw.Close()
Catch ex As Exception
System.Diagnostics.Debug.WriteLine(ex)
End Try
End Sub
You can draw multiple images to another one, once you have done that just use this sub:
Code:
Private Sub CreateIcon(ByVal bitmapName As String)
Try
Dim fi As New System.IO.FileInfo(bitmapName)
Dim bmp As New Bitmap(fi.FullName)
Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(fi.FullName.Replace(fi.Extension, ".ico"))
Icon.FromHandle(bmp.GetHicon).Save(sw.BaseStream)
sw.Close()
Catch ex As Exception
System.Diagnostics.Debug.WriteLine(ex)
End Try
End Sub
Right, except that all 3 sizes will be showing at the same time for my exe. I need 3 images in the 1 file, so when windows explorer views my exe file in list view the 16x16 image is showing, but when it's set to icon/small view the 32x32 icon is showing (same exe) or when the exe file the 48x48 icon is what's showing from the icon file.
Not all 3 icon sizes showing at the same time, only 1 of the 3 from inside is showing at any time, depending on Windows. It's an application icon.
Currently using VS 2015 Enterprise on Win10 Enterprise x64.
Interesting, I've always wondered how icons work in this way. I usually use a Photoshop plugin to create my high-quality (from transparent PNG images) icon files (compatible with Vista and up), but as far as I know it does not allow you to put multiple icons into a single file.
I doubt that there is any built in functionality in the framework, so you will probably need to know exactly how the ICO filetype is built, or you need to find a third party implementation that will do it for you.
I'll have a quick look, as this would be good to have.
The Wikipedia entry covers it pretty well in case you end up writing your own implementation.
I started to work on this myself actually. I found an implementation in Java but I didn't look at it yet. For once I'm going to try to figure it out for myself just to see how far I can get.
I figured reading ICO files would be the first step in understanding how they were build, so I implemented that first. I have not implemented reading the BMP data yet*, but so far it seems to get the type (1 for ICO, 2 for CUR files), the amount of images, and the sizes of the images.
vb.net Code:
Imports System.Drawing
Imports System.IO
Public Class IcoFile
Public Sub New(ByVal type As Integer, ByVal images As IEnumerable(Of Image))
If type <> 1 AndAlso type <> 2 Then Throw New ArgumentOutOfRangeException("type", "Argument 'type' must be 1 (ICO) or 2 (CUR).")
_Type = type
_Images = images
End Sub
#Region " Properties "
Private _Type As Integer
Public ReadOnly Property Type() As Integer
Get
Return _Type
End Get
End Property
Private _Images As IEnumerable(Of Image)
Public ReadOnly Property Images() As IEnumerable(Of Image)
Get
Return _Images
End Get
End Property
Public ReadOnly Property ImageCount() As Integer
Get
If Me.Images Is Nothing Then Return 0
Return Me.Images.Count
End Get
End Property
#End Region
Public Shared Function Read(ByVal path As String) As IcoFile
Dim type As Integer
Dim imageCount As Integer
Dim images As New List(Of Image)
Using input = File.OpenRead(path)
Using reader As New BinaryReader(input)
reader.ReadBytes(2) ' Reserved, should be 0
type = reader.ReadInt16() ' Icon type (1 for ICO, 2 for CUR)
imageCount = reader.ReadInt16() ' Number of images
For i As Integer = 0 To imageCount - 1
reader.BaseStream.Position = 6 + 16 * i ' 6 for the first 6 bytes, then 16 extra every iteration
If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim ico As IcoLibrary.IcoFile = IcoLibrary.IcoFile.Read(ofd.FileName)
MessageBox.Show(ico.Type.ToString)
For Each img As Image In ico.Images
MessageBox.Show(img.Size.ToString())
Next
End If
End Using
Tested it on my own icon which I knew to be 256x256 and returned that size. Then tested it on the icon of a game, which I was guessing would have all sizes, and it returned 256x256, 128x128, 48x48, 32x32, 24x24 and 16x16. It seems to work so far!
*Reading the BMP data is probably more difficult, but so far it seems you just need to read the offset first (to figure out where it's stored) and then just read from there. I just don't really understand what that data would return. I don't think its the bytes for the image just yet as there is all kinds of bitwise maps (AND and XOR maps or something) and transparency and that stuff... Might try that tomorrow.
As for writing, you can probably just do the same in reverse. Shouldn't be too hard once you understand which data goes where.
Not very far. I can read icons just fine, using my own code, or some code I found on the web, or using APIs from Chris' API pack. But there doesn't seem to be a way to write icons. I can't figure out how to write the data on my own, and the sources online I find all don't implement any save functions. Most of them do have them, but they either throw a NotImplementedException, or there's just no code in them. In one source, there was code to save, but there was no way to provide any list of icons... So still pretty useless :\
So in short, I can read just fine, but I cannot write them.
Here's the best one of the few I have. It's not my own code, I translated it from here and renamed some things. It worked on all icons I threw at it so far, including transparent ones (which my own code had problems with).
Basically, the IcoFile class is what represents one ICO file, containing a bunch of IconDeviceImages in an IconDeviceImageCollection. The IconDeviceImage class represents a single image in the icon.
The IcoFile class has a Save method which should save it back to the specified path, but I haven't tried it yet because I don't know how to actually change the ICO file (eg: change or add new images). Probably you'd have manually add a new instance of the IconDeviceImage class, but I've no idea how to determine how to set its properties... There is a constructor that accepts an Icon, but there's no code in there so that doesn't do anything...
Haven't looked at the source yet but it seems again read-only, and no saving implemented... Correct me if I'm wrong
I haven't fiddled with much here, but it looks like in that link it'd be just a matter of adding code for more iconEntity's to be added to the collection, update the header count and as for actually writing a file, create a file stream and write the bytes back out starting with the header info then loop the collection and write that info out.
I've taken a glance at your code there Nick and I gotta say that I'm not quite able to follow all of that, is there a specific class that I use or am I missing something? There's a lot of api calls too
Currently using VS 2015 Enterprise on Win10 Enterprise x64.
The IcoFile class is the one you need, the rest is just used internally. The IconDeviceImage class represents one image (including header information, and some stuff to write/read it), and the IcoFile class has a collection of those so that represents one ICO file.
As for writing; yeah you'd just need to write the bytes in the correct order, but the problem is that I don't know what values to use, nor how to convert an actual image to a byte stream in the correct format. From my reading attempts, you cannot simply use Image.FromStream or something like that as the bytes are not in the usual image format (they contain their own header and stuff which you need to write out too).