从文件中提取特定图标图层,然后将其另存为具有透明度的.ico文件

正如问题的标题所示,我正在尝试从文件中提取特定的图标图层,然后将其保存为具有透明度的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  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. 

你需要一个工具

没有人可以重现这个问题的原因是它需要一个精致的艺术图标。 一个简单的图标将使从ico到磁盘的行程变得很好并保持透明度。 我们也不知道你做了什么来获得质量差的图像 – 这是一个JPG,所以某处有一个Icon to JPG转换。

所以,我去寻找所显示的图像。 Google为我找到的Plixia crapware似乎不一样,所以我下载了JDownloader,它在评论中有一个与图片链接匹配的图标:

在此处输入图像描述

不仅透明度丢失了,它看起来一直很糟糕,它在资源管理器中的图像视图同样糟糕。 我们都知道ICO文件是一个可容纳大量图像的容器,但事实certificate, 图标是复杂的生物,并且没有用于详细/复杂图像的高质量编码器。

IconLib

我找到了相当数量的C / C ++代码来管理/争论图标,但是还有一个NET lib来为你做这个。 这个主题的post很少(很少),但它们似乎都与死链接或商业产品有关。 但是, CodeProject中的IconLib似乎可以满足您的需求。 简要地看一下创建/绘制图标的源代码,它看起来非常像C / C ++代码。

它可用于进行提取并将图标保存到集合中。 我忽略了那一部分,因为这个问题似乎只对SHDefExtractIcon感兴趣。 它可能有点过时,你可能想为.NET 4.xxx重新编译它; 但似乎确实写出了高质量的图标。 代码以SHDefExtractIcon的图标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.CreateFromSave方法来创建自己的版本,或者使用它来代替PInvoke。 我本来喜欢在蛇/蠕虫图像上进行测试,因为它背景看起来不仅仅是失去了透明度。


资源

MSDN上的图标

CodeProject上的IconLib

我试图重现你的问题,但没有你原来的exe或图标文件,很难这样做。 我过去确实遇到过这个问题。 在某些情况下,在Visual Basic或C#中保存图标(特别是在旧版本(如2008或2010)上)以256色图标结束。 我最终通过寻找一个外部图像管理库(如FreeImage)来解决这个问题,我将其插入到我的项目中以替换MS。

FreeImage是100%开源的,可在此处获取: http : //freeimage.sourceforge.net/ (请参阅下面有关如何将其引用到您的项目中的说明)。

以下是如何在VB.net中使用它的示例:

  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插入项目的方法。

  • 从项目站点下载源代码并进行编译。 如果您的项目是64位,请务必编译为64位,反之为32位。
  • 找到FreeImage.dll并将其移动到您的C#或vb.net项目文件夹。
  • Visual Studio中的.Net项目无法直接引用此dll。 您还必须构建FreeImageNET.dll,它包含在与FreeImage.dll相同的文件夹中,并在Visual Studio中引用。 FreeImageNET.dll的源代码可以在Wrapper / FreeImage.NET / cs下查找。 FreeImageNET.dll可以在anycpu架构(64 + 32位)或64 / 32bit下单独编译。