正如问题标题所示,我正在尝试从文件中提取特定的图标图层,然后将其保存为具有透明度的ico文件(如源图标所示)。
有许多与图标提取相关的问题,但这是针对以下使用 SHDefExtractIcon 功能的代码所特有的。
我遇到的问题是生成的.ico文件的颜色是错误的,它产生了一种半透明的可怕透明度,另一方面,生成的.png文件被完美保存。
这是生成的PNG文件:
这是生成的ICO文件:
这是Windows API的限制,还是我做错了什么?。
C#:
[DllImport("Shell32.dll", SetLastError = false)]
public static extern int SHDefExtractIcon(string iconFile, int iconIndex, uint flags, ref IntPtr hiconLarge, ref IntPtr hiconSmall, uint iconSize);
IntPtr hiconLarge = default(IntPtr);
SHDefExtractIcon("C:\\file.exe", 0, 0, hiconLarge, null, 256);
// ToDO: Handle HRESULT.
Icon ico = Icon.FromHandle(hiconLarge);
Bitmap bmp = ico.ToBitmap();
// Save as .png with transparency. success.
bmp.Save("C:\\ico.png", ImageFormat.Png);
// 1st intent: Save as .ico with transparency. failure.
//' Transparency is ok but it generates a false icon, it's .png with modified extension to .ico.
bmp.Save("C:\\ico1.ico", ImageFormat.Icon);
// 2nd intent: Save as .ico with transparency. failure. Wrong transparency.
using (MemoryStream ms = new MemoryStream()) {
ico.Save(ms);
using (FileStream fs = new FileStream("C:\\ico2.ico", FileMode.CreateNew)) {
ms.WriteTo(fs);
}
// ToDO: Destroy hiconLarge here with DestroyIcon function.
}
VB.NET:
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
<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)
' ToDO: Handle HRESULT.
Dim ico As Icon = Icon.FromHandle(hiconLarge)
Dim bmp As Bitmap = ico.ToBitmap()
' Save as .png with transparency. success.
bmp.Save("C:\ico.png", ImageFormat.Png)
' 1st intent: Save as .ico with transparency. failure.
' Transparency is ok but it generates a false icon, it's .png with modified extension to .ico.
bmp.Save("C:\ico1.ico", ImageFormat.Icon)
' 2nd intent: Save as .ico with transparency. failure. Wrong transparency.
Using ms As New MemoryStream
ico.Save(ms)
Using fs As New FileStream("C:\ico2.ico", FileMode.CreateNew)
ms.WriteTo(fs)
End Using
End Using
' ToDO: Destroy hiconLarge here with DestroyIcon function.
答案 0 :(得分:5)
您需要一个工具
没有人可以重现这个问题的原因是它需要一个精致的艺术图标。一个简单的图标将使从ico到磁盘的行程变得很好并保持透明度。我们也不知道你做了什么来获得质量差的图像 - 这是一个JPG,所以有一个图标到JPG转换。
所以,我去寻找所显示的图像。谷歌找到的Plixia crapware似乎不一样,所以我下载了JDownloader,它在评论中有一个与图片链接匹配的图标:
不仅透明度丢失,它看起来一直很糟糕,它在资源管理器中的图像视图同样糟糕。我们都知道ICO文件是一个可容纳大量图像的容器,但事实证明,Icons are complex critters并没有用于详细/复杂图像的高质量编码器。
我找到了相当数量的C / C ++代码来管理/争论图标,但是还有一个NET lib可以为你做这个。这个主题的帖子很少(很少),但它们似乎都与死链接或商业产品有关。但是,IconLib from CodeProject似乎可以做你想要的。简要地看一下创建/绘制图标的源代码,它看起来非常像C / C ++代码。
它可用于进行提取并将图标保存到集合中。我忽略了那一部分,因为这个问题似乎只对SHDefExtractIcon
感兴趣。它可能有点过时,你可能想为.NET 4.xxx重新编译它;但似乎确实写出了高质量的图标。代码以SHDefExtractIcon
中的图标开头,将其保存到文件并显示文件版本:
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
结果:
您可能会使用SingleIcon.CreateFrom
和Save
方法来创建自己的版本,或者使用它来代替PInvoke。我本来喜欢在蛇/蠕虫图像上进行测试,因为它背景看起来不仅仅是失去了透明度。
<强>资源强>
MSDN上的 CodeProject上的答案 1 :(得分:2)
我试图重现你的问题,但没有原来的exe或图标文件,很难这样做。我过去确实遇到过这个问题。在某些情况下,在Visual Basic或C#中保存图标(特别是在旧版本(如2008或2010)上)以256色图标结束。我最终通过寻找一个外部图像管理库(如FreeImage)来解决这个问题,我将其插入到我的项目中以替换MS。
FreeImage是100%开源的,可在此处获取:http://freeimage.sourceforge.net/(请参阅下面有关如何在项目中引用它的说明)。
以下是如何在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
这就是将FreeImage插入项目的方法。