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

Ele*_*ios 5 .net c# vb.net winapi icons

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

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.
Run Code Online (Sandbox Code Playgroud)

Ňɏs*_*arp 5

你需要一个工具

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

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

在此输入图像描述

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

IconLib

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

结果:

在此输入图像描述在此输入图像描述

你也许可以将婴儿床SingleIcon.CreateFromSave方法来创建自己的版本,并或许到位的PInvoke的使用它.我本来喜欢在蛇/蠕虫图像上测试它,因为它的背景看起来不仅仅是失去了透明度.


资源

MSDN上的图标

CodeProject上的IconLib