As the title of the question says, I\'m trying to extract a specific icon layer from file then save it as ico file with transparency (as the source icon have).
There
I was trying to reproduce your question but without your original exe or icon file, it's difficult to do so. I did have this issue in the past though. There are some instances where saving the icon in Visual Basic or C# (especially on older editions such as 2008 or 2010) ended with a 256 color icon. I ended up resolving this by going for an external image management library such as FreeImage which I plug into my project to replace the MS one.
FreeImage is 100% open source and available here: http://freeimage.sourceforge.net/ (see the instructions on how to reference it into your project below).
Here is an example of how to use it in VB.net:
<DllImport("Shell32.dll", SetLastError:=False)>
Public Shared Function SHDefExtractIcon(ByVal iconFile As String,
ByVal iconIndex As Integer,
ByVal flags As UInteger,
ByRef hiconLarge As IntPtr,
ByRef hiconSmall As IntPtr,
ByVal iconSize As UInteger
) As Integer
End Function
Dim hiconLarge As IntPtr
SHDefExtractIcon("C:\file.exe", 0, 0, hiconLarge, Nothing, 256)
Dim ico As Icon = Icon.FromHandle(hiconLarge)
Dim bmp As Bitmap = ico.ToBitmap()
Dim fiBmp As FreeImageAPI.FreeImageBitmap = New FreeImageAPI.FreeImageBitmap(bmp)
' At this point you can either save a simple one size icon file or you can
' take advantage of the FreeImage engine to save a multi-sized icon:
fiBmp.Save("C:\ico1.ico", FreeImageAPI.FREE_IMAGE_FORMAT.FIF_ICO)
' To add more layers to your Icon:
fiBmp.Rescale(128, 128, FreeImageAPI.FREE_IMAGE_FILTER.FILTER_LANCZOS3)
fiBmp.SaveAdd("C:\ico1.ico")
fiBmp.Rescale(64, 64, FreeImageAPI.FREE_IMAGE_FILTER.FILTER_LANCZOS3)
fiBmp.SaveAdd("C:\ico1.ico")
fiBmp.Rescale(48, 48, FreeImageAPI.FREE_IMAGE_FILTER.FILTER_LANCZOS3)
fiBmp.SaveAdd("C:\ico1.ico")
'etc etc etc
This is how you plug FreeImage into your project.
You need a tool
The reason no one could reproduce the problem is that it requires an elaborate, artistic icon. A simple icon will make the trip from ico to disk to image just fine and preserve transparency. We also do not know what you did to get the poor quality image - it is a JPG here so somewhere there is an Icon to JPG transform.
So, I went looking for the image shown. The Plixia crapware Google found for me seems not to be the same, so I downloaded JDownloader which did have an icon matching an image link in comments:
Not only is the transparency lost, it looks bad all way around and the Image view of it in Explorer is just as bad. We all know an ICO file is a container which can hold a number of images, but as it turns out, Icons are complex critters and there is no high quality encoder for detailed/complex images.
I found a fair amount of C/C++ code to manage/wrangle icons, but there is also a NET lib to do this for you. There are a (very) few posts here on this topic, but they all seem link to dead links or commercial products. However, IconLib from CodeProject seems to do what you want. Looking briefly at the source code to create/draw the icon, it looks very much like that C/C++ code.
It can be used to do the extraction(s) and save icons to a collection. I ignored that part since the question seems interested only in SHDefExtractIcon
. It might be a little out of date and you may want to recompile it for NET 4.xxx; but it does seem to write out a high quality icon. Code to start with an icon from SHDefExtractIcon
, save it to file and show the file version:
Dim file As String = "C:\Temp\electro crapware\JDownloader2.exe"
Dim hiconLarge As IntPtr
NativeMethods.SHDefExtractIcon(file, 0, 0, hiconLarge, Nothing, 256)
Dim ico As Icon = Icon.FromHandle(hiconLarge)
Dim bmp As Bitmap = ico.ToBitmap()
pbIcoA.Image = bmp
' png version
bmp.Save("C:\Temp\electro.png", ImageFormat.Png)
'' simple NET ico save: Doesnt Work! (== low quality image)
'Using fs As New FileStream("C:\Temp\electro.ico", FileMode.OpenOrCreate)
' ico.Save(fs)
'End Using
'' Imports System.Drawing.IconLib
Dim myIcons As New MultiIcon()
Dim sIco As SingleIcon = myIcons.Add("electro")
' create Icon from the PInvoked ico.ToBitmap()
' note that enum only goes "up" to Vista
sIco.CreateFrom(bmp, IconOutputFormat.Vista)
sIco.Save("C:\Temp\electro2.ico")
' now display image from disk ico
Dim bmpX As Bitmap
Using icob As New Icon("C:\Temp\electro2.ico")
bmpX = icob.ToBitmap
End Using
pbIcoB.Image = bmpX
' ToDo: Dispose
Results:
You could probably crib the SingleIcon.CreateFrom
and Save
methods to create your own versions and or maybe use it in place of PInvoke. I would have loved to test it on the snake/worm image because that background looks like it suffers from more than a loss of transparency.
Resources
Icons on MSDN
IconLib on CodeProject