【问题标题】:Using a 256 x 256 Windows Vista icon in an application在应用程序中使用 256 x 256 Windows Vista 图标
【发布时间】:2017-10-08 18:44:09
【问题描述】:

我有一个应用程序,我为其制作了 256 x 256 Windows Vista 图标。

我想知道如何在用作应用程序图标的 ico 文件中使用 256x256 PNG 文件并将其显示在表单上的图片框中。

我正在使用 VB.NET,但 C# 中的答案很好。我想我可能不得不使用反射。

我不确定这在 Windows XP 中是否可行,并且可能需要 Windows Vista API

【问题讨论】:

  • 我很确定有专门用于执行此操作的框架方法。我以前在这里看到过。
  • 目前我看到框架方法只能取出32x32的图标:(
  • 请注意,.NET 4.6 更改了 Icon.ToBitmap 函数以允许它“看到”PNG 图标(128x128 和 256x256)。 msdn.microsoft.com/en-us/library/…

标签: c# vb.net png icons


【解决方案1】:

今天,我做了一个非常好的从 Vista 图标中提取 256x256 位图的功能。

和你一样,Nathan W,我用它在“关于”框中将大图标显示为位图。例如,此代码将 Vista 图标获取为 PNG 图像,并将其显示在 256x256 的 PictureBox 中:

picboxAppLogo.Image = ExtractVistaIcon(myIcon);

此函数将 Icon 对象作为参数。因此,您可以将它与任何图标一起使用 - 来自资源、来自文件、来自流等等。 (阅读下文了解如何提取 EXE 图标)。

它可以在任何操作系统上运行,因为它使用任何 Win32 API,它是100% 托管代码 :-)

// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx

Bitmap ExtractVistaIcon(Icon icoIcon)
{
    Bitmap bmpPngExtracted = null;
    try
    {
        byte[] srcBuf = null;
        using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            { icoIcon.Save(stream); srcBuf = stream.ToArray(); }
        const int SizeICONDIR = 6;
        const int SizeICONDIRENTRY = 16;
        int iCount = BitConverter.ToInt16(srcBuf, 4);
        for (int iIndex=0; iIndex<iCount; iIndex++)
        {
            int iWidth  = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex];
            int iHeight = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex + 1];
            int iBitCount   = BitConverter.ToInt16(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 6);
            if (iWidth == 0 && iHeight == 0 && iBitCount == 32)
            {
                int iImageSize   = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 8);
                int iImageOffset = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 12);
                System.IO.MemoryStream destStream = new System.IO.MemoryStream();
                System.IO.BinaryWriter writer = new System.IO.BinaryWriter(destStream);
                writer.Write(srcBuf, iImageOffset, iImageSize);
                destStream.Seek(0, System.IO.SeekOrigin.Begin);
                bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
                break;
            }
        }
    }
    catch { return null; }
    return bmpPngExtracted;
}

重要提示!如果您想直接从 EXE 文件加载此图标,那么您不能使用 Icon.ExtractAssociatedIcon(Application.ExecutablePath) em> 作为参数,因为 .NET 函数 ExtractAssociatedIcon() 太笨了,它只提取 32x32 图标!

相反,您最好使用由 Tsuda Kageyu (http://www.codeproject.com/KB/cs/IconExtractor.aspx) 创建的整个 IconExtractor 类。您可以稍微简化这个类,使其更小。以这种方式使用 IconExtractor

// Getting FILL icon set from EXE, and extracting 256x256 version for logo...
using (TKageyu.Utils.IconExtractor IconEx = new TKageyu.Utils.IconExtractor(Application.ExecutablePath))
{
    Icon icoAppIcon = IconEx.GetIcon(0); // Because standard System.Drawing.Icon.ExtractAssociatedIcon() returns ONLY 32x32.
    picboxAppLogo.Image = ExtractVistaIcon(icoAppIcon);
}

注意:我在这里仍然使用我的 ExtractVistaIcon() 函数,因为我不喜欢 IconExtractor 处理这项工作的方式 - 首先,它通过使用 IconExtractor.SplitIcon(icoAppIcon ),然后您必须知道确切的 256x256 图标索引才能获得所需的 vista-icon。所以,在这里使用我的 ExtractVistaIcon() 会更快更简单:)

【讨论】:

  • 看起来很有希望,但对我不起作用。我运行的是 Windows 7。你确定你已经测试过这段代码吗?
  • 实际上,它从 Vista ico 文件中提取了一个 256x256 的图像,这很好,但它并不能解决从可执行文件中获取全套图标图像的问题。 .ExtractAssociatedIcon 仅从文件中提取 32x32 图像。
  • 是的,抱歉,我忘记更新我的解决方案了。现在它已修复,并更新了有关直接从 EXE 文件获取 FULL 图标集的特别说明。
  • 您知道为什么此例程不适用于不同的位深度吗?例如,如果我修改代码以忽略位深度检查,并尝试使用 1 位 256x256 图标,则在尝试创建位图时会出现“参数无效”异常。
  • 如果我首先创建一个图标流然后将其转换为位图,它就可以工作。不知道为什么,但它有效。
【解决方案2】:

找到信息here。要获得大的 Vista 图标,您需要使用 Shell32 的 SHGetFileInfo 方法。我已经复制了下面的相关文本,当然您需要将文件名变量替换为“Assembly.GetExecutingAssembly().Location”。

using System.Runtime.InteropServices;

我们将在调用 SHGetFileInfo() 时使用一组常量来指定我们希望检索的图标的大小:

// Constants that we need in the function call
private const int SHGFI_ICON = 0x100;
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;

SHFILEINFO 结构非常重要,因为它将是我们处理各种文件信息的句柄,其中包括图形图标。

// This structure will contain information about the file
public struct SHFILEINFO
{
    // Handle to the icon representing the file
    public IntPtr hIcon;
    // Index of the icon within the image list
    public int iIcon;
    // Various attributes of the file
    public uint dwAttributes;
    // Path to the file
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string szDisplayName;
    // File type
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    public string szTypeName;
};

非托管代码的最后准备是定义SHGetFileInfo的签名,它位于流行的Shell32.dll中:

// The signature of SHGetFileInfo (located in Shell32.dll)
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags);

现在我们已经准备好了一切,是时候调用函数并显示我们检索到的图标了。将被检索的对象是一个图标类型 (System.Drawing.Icon),但我们希望在 PictureBox 中显示它,因此我们将使用 ToBitmap() 方法将图标转换为位图。

但首先,您需要将 3 个控件添加到表单中,一个按钮 btnExtract 的 Text 属性具有“提取图标”,picIconSmall 是一个 PictureBox,一个 picIconLarge 也是一个 PictureBox。那是因为我们会得到两个图标大小。现在在 Visual Studio 的设计视图中双击 btnExtract,您将看到它的 Click 事件。里面是剩下的代码:

private void btnExtract_Click(object sender, EventArgs e)
{
    // Will store a handle to the small icon
    IntPtr hImgSmall;
    // Will store a handle to the large icon
    IntPtr hImgLarge;

    SHFILEINFO shinfo = new SHFILEINFO();

    // Open the file that we wish to extract the icon from
    if(openFile.ShowDialog() == DialogResult.OK)
    {
        // Store the file name
        string FileName = openFile.FileName;
        // Sore the icon in this myIcon object
        System.Drawing.Icon myIcon;

        // Get a handle to the small icon
        hImgSmall = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SMALLICON);
        // Get the small icon from the handle
        myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
        // Display the small icon
        picIconSmall.Image = myIcon.ToBitmap();

        // Get a handle to the large icon
        hImgLarge = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON);
        // Get the large icon from the handle
        myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
        // Display the large icon
        picIconLarge.Image = myIcon.ToBitmap();

    }
}

更新:找到更多信息here

【讨论】:

    【解决方案3】:

    以上答案都不能处理 Vista 图标 - 只有小 (32x32) 和大 (48x48)

    有一个库可以处理 Vista 图标 here

    ...由于是双png alpha 通道格式,它看起来相当复杂。

    我会尝试在 vb .net 中做出简明的回答,但可能需要一些时间。

    【讨论】:

    • 我的解决方案 (ExtractVistaIcon()) 我们主要基于更简单的类“IconExtractor”(比“IconLib”简单得多)。我已经更新了我的答案,添加了有关直接从可执行文件中获取 256x256 图标的信息。
    【解决方案4】:

    遇到在图片框中显示来自 ICO 文件的 256*256*32 图像的相同问题,我发现 SAL80 的解决方案是最有效的(并且几乎可以工作)。但是,原始代码不支持存储为 BMP 的图像(大图标通常是 PNG,但并非总是...)。

    这是我的版本以供将来参考。创建位图的代码也稍微简单一些:

        /// <summary>
        /// Extracts  the large Vista icon from a ICO file 
        /// </summary>
        /// <param name="srcBuf">Bytes of the ICO file</param>
        /// <returns>The large icon or null if not found</returns>
        private static Bitmap ExtractVistaIcon(byte[] srcBuf)
        {
            const int SizeIcondir = 6;
            const int SizeIcondirentry = 16;
    
            // Read image count from ICO header
            int iCount = BitConverter.ToInt16(srcBuf, 4);
    
            // Search for a large icon
            for (int iIndex = 0; iIndex < iCount; iIndex++)
            {
                // Read image information from image directory entry
                int iWidth = srcBuf[SizeIcondir + SizeIcondirentry * iIndex];
                int iHeight = srcBuf[SizeIcondir + SizeIcondirentry * iIndex + 1];
                int iBitCount = BitConverter.ToInt16(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 6);
    
                // If Vista icon
                if (iWidth == 0 && iHeight == 0 && iBitCount == 32)
                {
                    // Get image data position and length from directory
                    int iImageSize = BitConverter.ToInt32(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 8);
                    int iImageOffset = BitConverter.ToInt32(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 12);
    
                    // Check if the image has a PNG signature
                    if (srcBuf[iImageOffset] == 0x89 && srcBuf[iImageOffset+1] == 0x50 && srcBuf[iImageOffset+2] == 0x4E && srcBuf[iImageOffset+3] == 0x47)
                    {
                        // the PNG data is stored directly in the file
                        var x = new MemoryStream(srcBuf, iImageOffset, iImageSize, false, false);
                        return new Bitmap(x); 
                    }
    
                    // Else it's bitmap data with a partial bitmap header
                    // Read size from partial header
                    int w = BitConverter.ToInt32(srcBuf, iImageOffset + 4);
                    // Create a full header
                    var b = new Bitmap(w, w, PixelFormat.Format32bppArgb);
                    // Copy bits into bitmap
                    BitmapData bmpData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.WriteOnly, b.PixelFormat);
                    Marshal.Copy(srcBuf, iImageOffset + Marshal.SizeOf(typeof(Bitmapinfoheader)), bmpData.Scan0, b.Width*b.Height*4);
                    b.UnlockBits(bmpData);
                    return b;
                }
            }
    
            return null;
        }
    

    【讨论】:

    【解决方案5】:

    查看可用的 Windows icon functions。还有一个overview 提到查询不同的图标大小。有一个 Dream.In.Code 论坛线程用于在 C# 中使用 API,还有一个 Pinvoke.net reference

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-21
      • 1970-01-01
      • 2010-09-29
      • 2019-02-12
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 2020-05-12
      相关资源
      最近更新 更多